lang->line('current_version') . ' ';
- echo $this->config->item('ea_version');
- $release_title = $this->config->item('ea_release_title');
+ echo $this->config->item('version');
+ $release_title = $this->config->item('release_label');
if ($release_title != '') {
echo ' - ' . $release_title;
}
diff --git a/src/application/views/errors/cli/error_404.php b/src/application/views/errors/cli/error_404.php
new file mode 100644
index 00000000..6984b61e
--- /dev/null
+++ b/src/application/views/errors/cli/error_404.php
@@ -0,0 +1,8 @@
+
+
+An uncaught Exception was encountered
+
+Type:
+Message:
+Filename: getFile(), "\n"; ?>
+Line Number: getLine(); ?>
+
+
+
+Backtrace:
+getTrace() as $error): ?>
+
+ File:
+ Line:
+ Function:
+
+
+
+
diff --git a/src/application/views/errors/cli/error_general.php b/src/application/views/errors/cli/error_general.php
new file mode 100644
index 00000000..6984b61e
--- /dev/null
+++ b/src/application/views/errors/cli/error_general.php
@@ -0,0 +1,8 @@
+
+
+A PHP Error was encountered
+
+Severity:
+Message:
+Filename:
+Line Number:
+
+
+
+Backtrace:
+
+
+ File:
+ Line:
+ Function:
+
+
+
+
diff --git a/src/application/views/errors/cli/index.html b/src/application/views/errors/cli/index.html
new file mode 100644
index 00000000..b702fbc3
--- /dev/null
+++ b/src/application/views/errors/cli/index.html
@@ -0,0 +1,11 @@
+
+
+
+
403 Forbidden
+
+
+
+
Directory access is forbidden.
+
+
+
diff --git a/src/application/errors/error_404.php b/src/application/views/errors/html/error_404.php
similarity index 100%
rename from src/application/errors/error_404.php
rename to src/application/views/errors/html/error_404.php
diff --git a/src/application/errors/error_db.php b/src/application/views/errors/html/error_db.php
similarity index 100%
rename from src/application/errors/error_db.php
rename to src/application/views/errors/html/error_db.php
diff --git a/src/application/views/errors/html/error_exception.php b/src/application/views/errors/html/error_exception.php
new file mode 100644
index 00000000..87848866
--- /dev/null
+++ b/src/application/views/errors/html/error_exception.php
@@ -0,0 +1,32 @@
+
+
+
+
+
An uncaught Exception was encountered
+
+
Type:
+
Message:
+
Filename: getFile(); ?>
+
Line Number: getLine(); ?>
+
+
+
+
Backtrace:
+ getTrace() as $error): ?>
+
+
+
+
+ File:
+ Line:
+ Function:
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/application/errors/error_general.php b/src/application/views/errors/html/error_general.php
similarity index 100%
rename from src/application/errors/error_general.php
rename to src/application/views/errors/html/error_general.php
diff --git a/src/application/errors/error_php.php b/src/application/views/errors/html/error_php.php
similarity index 100%
rename from src/application/errors/error_php.php
rename to src/application/views/errors/html/error_php.php
diff --git a/src/application/errors/index.html b/src/application/views/errors/html/index.html
similarity index 100%
rename from src/application/errors/index.html
rename to src/application/views/errors/html/index.html
diff --git a/src/assets/js/backend.js b/src/assets/js/backend.js
index dfcc665a..88450764 100644
--- a/src/assets/js/backend.js
+++ b/src/assets/js/backend.js
@@ -9,70 +9,75 @@
* @since v1.0.0
* ---------------------------------------------------------------------------- */
-/**
- * Main javascript code for the backend of Easy!Appointments.
- */
-$(document).ready(function() {
- if (window.console === undefined) {
- window.console = function() {} // IE compatibility
- }
+window.Backend = window.Backend || {};
- $(window).resize(function() {
- Backend.placeFooterToBottom();
- }).trigger('resize');
+ /**
+ * This module contains functions that are used in the backend section of
+ * the application.
+ *
+ * @module Backend
+ */
+(function(exports) {
- $(document).ajaxStart(function() {
- $('#loading').show();
- });
+ 'use strict';
- $(document).ajaxStop(function() {
- $('#loading').hide();
- });
-
- $('.menu-item').qtip({
- position: {
- my: 'top center',
- at: 'bottom center'
- },
- style: {
- classes: 'qtip-green qtip-shadow custom-qtip'
+ /**
+ * Main javascript code for the backend of Easy!Appointments.
+ */
+ $(document).ready(function() {
+ if (window.console === undefined) {
+ window.console = function() {} // IE compatibility
}
+
+ $(window).resize(function() {
+ Backend.placeFooterToBottom();
+ }).trigger('resize');
+
+ $(document).ajaxStart(function() {
+ $('#loading').show();
+ });
+
+ $(document).ajaxStop(function() {
+ $('#loading').hide();
+ });
+
+ $('.menu-item').qtip({
+ position: {
+ my: 'top center',
+ at: 'bottom center'
+ },
+ style: {
+ classes: 'qtip-green qtip-shadow custom-qtip'
+ }
+ });
+
+ GeneralFunctions.enableLanguageSelection($('#select-language'));
});
- GeneralFunctions.enableLanguageSelection($('#select-language'));
-});
-
-/**
- * This namespace contains functions that are used in the backend section of
- * the applications.
- *
- * @namespace Backend
- */
-var Backend = {
/**
* Backend Constants
*/
- DB_SLUG_ADMIN: 'admin',
- DB_SLUG_PROVIDER: 'provider',
- DB_SLUG_SECRETARY: 'secretary',
- DB_SLUG_CUSTOMER: 'customer',
+ exports.DB_SLUG_ADMIN = 'admin';
+ exports.DB_SLUG_PROVIDER = 'provider';
+ exports.DB_SLUG_SECRETARY = 'secretary';
+ exports.DB_SLUG_CUSTOMER = 'customer';
- PRIV_VIEW: 1,
- PRIV_ADD: 2,
- PRIV_EDIT: 4,
- PRIV_DELETE: 8,
+ exports.PRIV_VIEW = 1;
+ exports.PRIV_ADD = 2;
+ exports.PRIV_EDIT = 4;
+ exports.PRIV_DELETE = 8;
- PRIV_APPOINTMENTS: 'appointments',
- PRIV_CUSTOMERS: 'customers',
- PRIV_SERVICES: 'services',
- PRIV_USERS: 'users',
- PRIV_SYSTEM_SETTINGS: 'system_settings',
- PRIV_USER_SETTINGS: 'user_settings',
+ exports.PRIV_APPOINTMENTS = 'appointments';
+ exports.PRIV_CUSTOMERS = 'customers';
+ exports.PRIV_SERVICES = 'services';
+ exports.PRIV_USERS = 'users';
+ exports.PRIV_SYSTEM_SETTINGS = 'system_settings';
+ exports.PRIV_USER_SETTINGS = 'user_settings';
/**
* Place the backend footer always on the bottom of the page.
*/
- placeFooterToBottom: function() {
+ exports.placeFooterToBottom = function() {
var $footer = $('#footer');
if (window.innerHeight > $('body').height()) {
@@ -86,7 +91,7 @@ var Backend = {
'position': 'static'
});
}
- },
+ };
/**
* Display backend notifications to user.
@@ -100,7 +105,7 @@ var Backend = {
* to the user. Every array item is an object that contains the 'label' and
* 'function' key values.
*/
- displayNotification: function(message, actions) {
+ exports.displayNotification = function(message, actions) {
if (message == undefined) {
message = 'NO MESSAGE PROVIDED FOR THIS NOTIFICATION';
}
@@ -130,4 +135,5 @@ var Backend = {
$('#notification').html(notificationHtml);
$('#notification').show('blind');
}
-};
+
+})(window.Backend);
diff --git a/src/assets/js/general_functions.js b/src/assets/js/general_functions.js
index ccaad1c7..f151742e 100644
--- a/src/assets/js/general_functions.js
+++ b/src/assets/js/general_functions.js
@@ -9,33 +9,36 @@
* @since v1.0.0
* ---------------------------------------------------------------------------- */
+window.GeneralFunctions = window.GeneralFunctions || {};
+
/**
- * This file contains the General Functions javascript namespace.
- * It contains functions that apply both on the front and back
- * end of the application.
+ * General Functions Module
*
- * @namespace GeneralFunctions
+ * It contains functions that apply both on the front and back end of the application.
+ *
+ * @module GeneralFunctions
*/
-var GeneralFunctions = {
+(function(exports) {
+
+ 'use strict';
+
/**
* General Functions Constants
*/
- EXCEPTIONS_TITLE: EALang['unexpected_issues'],
- EXCEPTIONS_MESSAGE: EALang['unexpected_issues_message'],
- WARNINGS_TITLE: EALang['unexpected_warnings'],
- WARNINGS_MESSAGE: EALang['unexpected_warnings_message'],
+ exports.EXCEPTIONS_TITLE = EALang['unexpected_issues'];
+ exports.EXCEPTIONS_MESSAGE = EALang['unexpected_issues_message'];
+ exports.WARNINGS_TITLE = EALang['unexpected_warnings'];
+ exports.WARNINGS_MESSAGE = EALang['unexpected_warnings_message'];
/**
- * This functions displays a message box in
- * the admin array. It is usefull when user
+ * This functions displays a message box in the admin array. It is usefull when user
* decisions or verifications are needed.
*
* @param {string} title The title of the message box.
* @param {string} message The message of the dialog.
- * @param {array} messageButtons Contains the dialog
- * buttons along with their functions.
+ * @param {array} messageButtons Contains the dialog buttons along with their functions.
*/
- displayMessageBox: function(title, message, messageButtons) {
+ exports.displayMessageBox = function(title, message, messageButtons) {
// Check arguments integrity.
if (title == undefined || title == '') {
title = '
';
@@ -77,16 +80,14 @@ var GeneralFunctions = {
$('#message_box').dialog('open');
$('.ui-dialog .ui-dialog-buttonset button').addClass('btn btn-default');
$('#message_box .ui-dialog-titlebar-close').hide();
- },
+ };
/**
- * This method centers a DOM element vertically and horizontally
- * on the page.
+ * This method centers a DOM element vertically and horizontally on the page.
*
- * @param {object} elementHandle The object that is going to be
- * centered.
+ * @param {object} elementHandle The object that is going to be centered.
*/
- centerElementOnPage: function(elementHandle) {
+ exports.centerElementOnPage = function(elementHandle) {
// Center main frame vertical middle
$(window).resize(function() {
var elementLeft = ($(window).width() - elementHandle.outerWidth()) / 2;
@@ -100,7 +101,7 @@ var GeneralFunctions = {
});
});
$(window).resize();
- },
+ };
/**
* This function retrieves a parameter from a "GET" formed url.
@@ -109,24 +110,26 @@ var GeneralFunctions = {
*
* @param {string} url The selected url.
* @param {string} name The parameter name.
- * @returns {String} Returns the parameter value.
+
+ * @return {string} Returns the parameter value.
*/
- getUrlParameter: function(url, parameterName) {
+ exports.getUrlParameter = function(url, parameterName) {
parameterName = parameterName.replace(/[\[]/,'\\\[').replace(/[\]]/,'\\\]');
var regexS = '[\\#&]' + parameterName + '=([^]*)',
regex = new RegExp(regexS),
results = regex.exec(url);
return (results == null) ? '' : results[1];
- },
+ };
/**
- * This function creates a RFC 3339 date string. This string is needed
- * by the Google Calendar API in order to pass dates as parameters.
+ * This function creates a RFC 3339 date string. This string is needed by the Google Calendar API
+ in order to pass dates as parameters.
*
- * @param {date} dt The given date that will be transformed
- * @returns {String} Returns the transformed string.
+ * @param {date} dt The given date that will be transformed.
+
+ * @return {String} Returns the transformed string.
*/
- ISODateString: function(dt) {
+ exports.ISODateString = function(dt) {
function pad(n) {
return n<10 ? '0'+n : n;
}
@@ -137,19 +140,21 @@ var GeneralFunctions = {
+ pad(dt.getUTCHours())+':'
+ pad(dt.getUTCMinutes())+':'
+ pad(dt.getUTCSeconds())+'Z';
- },
+ };
/**
* This method creates and returns an exact copy of the provided object.
+
* It is very usefull whenever changes need to be made to an object without
* modyfing the original data.
*
* @link http://stackoverflow.com/questions/728360/most-elegant-way-to-clone-a-javascript-object
*
* @param {object} originalObject Object to be copied.
- * @returns {object} Returns an exact copy of the provided element.
+
+ * @return {object} Returns an exact copy of the provided element.
*/
- clone: function(originalObject) {
+ exports.clone = function(originalObject) {
// Handle the 3 simple types, and null or undefined
if (null == originalObject || 'object' != typeof originalObject)
return originalObject;
@@ -181,7 +186,7 @@ var GeneralFunctions = {
}
throw new Error('Unable to copy obj! Its type isn\'t supported.');
- },
+ };
/**
* This method validates an email address. If the address is not on the proper
@@ -190,22 +195,25 @@ var GeneralFunctions = {
* @link http://badsyntax.co/post/javascript-email-validation-rfc822
*
* @param {string} email The email address to be checked.
- * @returns {bool} Returns the validation result.
+
+ * @return {bool} Returns the validation result.
*/
- validateEmail: function (email) {
+ exports.validateEmail = function (email) {
var re = /^([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22))*\x40([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d))*$/;
return re.test(email);
- },
+ };
/**
- * This method returns the exception html display for javascript ajax calls.
+ * This method returns the exception HTML display for javascript ajax calls.
+
* It uses the Bootstrap collapse module to show exception messages when the
* user opens the "Details" collapse component.
*
* @param {array} exceptions Contains the exceptions to be displayed.
- * @returns {string} Returns the html markup for the exceptions.
+ *
+ * @return {string} Returns the html markup for the exceptions.
*/
- exceptionsToHtml: function(exceptions) {
+ exports.exceptionsToHtml = function(exceptions) {
var html =
'' +
'
' +
@@ -228,7 +236,7 @@ var GeneralFunctions = {
html += '
';
return html;
- },
+ };
/**
* This method parse the json encoded strings that are fetched by ajax calls.
@@ -236,7 +244,7 @@ var GeneralFunctions = {
* @param {array} exceptions Exception array returned by an ajax call.
* @returns {array} Returns the parsed js objects.
*/
- parseExceptions: function(exceptions) {
+ exports.parseExceptions = function(exceptions) {
var parsedExceptions = new Array();
$.each(exceptions, function(index, exception) {
@@ -244,7 +252,7 @@ var GeneralFunctions = {
});
return parsedExceptions;
- },
+ };
/**
* Makes the first letter of the string upper case.
@@ -252,20 +260,21 @@ var GeneralFunctions = {
* @param {string} str The string to be converted.
* @returns {string} Returns the capitalized string.
*/
- ucaseFirstLetter: function(str){
+ exports.ucaseFirstLetter = function(str){
return str.charAt(0).toUpperCase() + str.slice(1);
- },
+ };
/**
* All backend js code has the same way of dislaying exceptions that are raised on the
* server during an ajax call.
*
* @param {object} response Contains the server response. If exceptions or warnings are
- * found, user friendly messages are going to be displayed to the user.
- * @returns {bool} Returns whether the the ajax callback should continue the execution or
+ * found, user friendly messages are going to be displayed to the user.4
+ *
+ * @return {bool} Returns whether the the ajax callback should continue the execution or
* stop, due to critical server exceptions.
*/
- handleAjaxExceptions: function(response) {
+ exports.handleAjaxExceptions = function(response) {
if (response.exceptions) {
response.exceptions = GeneralFunctions.parseExceptions(response.exceptions);
GeneralFunctions.displayMessageBox(GeneralFunctions.EXCEPTIONS_TITLE, GeneralFunctions.EXCEPTIONS_MESSAGE);
@@ -280,7 +289,7 @@ var GeneralFunctions = {
}
return true;
- },
+ };
/**
* Enables the language selection functionality. Must be called on every page has a
@@ -289,7 +298,7 @@ var GeneralFunctions = {
*
* @param {object} $element Selected element button for the language selection.
*/
- enableLanguageSelection: function($element) {
+ exports.enableLanguageSelection = function($element) {
// Select Language
var html = '';
$.each(availableLanguages, function() {
@@ -330,7 +339,7 @@ var GeneralFunctions = {
}, 'json').fail(GeneralFunctions.ajaxFailureHandler);
});
- },
+ };
/**
* Use this method for common error handling between
@@ -339,7 +348,7 @@ var GeneralFunctions = {
* @param {string} textStatus
* @param {object} errorThrown
*/
- ajaxFailureHandler: function(jqxhr, textStatus, errorThrown) {
+ exports.ajaxFailureHandler = function(jqxhr, textStatus, errorThrown) {
var exceptions = [
{
message: 'AJAX Error: ' + errorThrown
@@ -348,7 +357,7 @@ var GeneralFunctions = {
GeneralFunctions.displayMessageBox(GeneralFunctions.EXCEPTIONS_TITLE,
GeneralFunctions.EXCEPTIONS_MESSAGE);
$('#message_box').append(GeneralFunctions.exceptionsToHtml(exceptions));
- },
+ };
/**
* Escape JS HTML string values for XSS prevention.
@@ -356,9 +365,9 @@ var GeneralFunctions = {
* @param {string} str String to be escaped.
* @returns {string} Returns the escaped string.
*/
- escapeHtml: function(str) {
+ exports.escapeHtml = function(str) {
return $('').text(str).html();
- },
+ };
/**
* Format a given date according to the date format setting.
@@ -367,9 +376,10 @@ var GeneralFunctions = {
* @param {string} dateFormatSetting The setting provided by PHP must be one of
* the "DMY", "MDY" or "YMD".
* @param {bool} addHours (optional) Whether to add hours to the result.
- * @returns {string} Returns the formatted date string.
+
+ * @return {string} Returns the formatted date string.
*/
- formatDate: function(date, dateFormatSetting, addHours) {
+ exports.formatDate = function(date, dateFormatSetting, addHours) {
var format, result,
hours = addHours ? ' HH:mm' : '';
@@ -388,5 +398,6 @@ var GeneralFunctions = {
}
return result;
- }
-};
+ };
+
+})(window.GeneralFunctions);
diff --git a/src/assets/js/installation.js b/src/assets/js/installation.js
index 9cafc370..473108b5 100644
--- a/src/assets/js/installation.js
+++ b/src/assets/js/installation.js
@@ -10,9 +10,11 @@
* ---------------------------------------------------------------------------- */
$(document).ready(function() {
- var MIN_PASSWORD_LENGTH = 7;
- var AJAX_SUCCESS = 'SUCCESS';
- var AJAX_FAILURE = 'FAILURE';
+ 'use strict';
+
+ var MIN_PASSWORD_LENGTH = 7,
+ AJAX_SUCCESS = 'SUCCESS',
+ AJAX_FAILURE = 'FAILURE';
$(document).ajaxStart(function() {
$('#loading').show();
@@ -26,14 +28,16 @@ $(document).ready(function() {
* Event: Install Easy!Appointments Button "Click"
*/
$('#install').click(function() {
- if (!validate()) return;
+ if (!validate()) {
+ return;
+ }
- var postUrl = GlobalVariables.baseUrl + '/index.php/installation/ajax_install';
- var postData = {
- 'csrfToken': GlobalVariables.csrfToken,
- 'admin': JSON.stringify(getAdminData()),
- 'company': JSON.stringify(getCompanyData())
- };
+ var postUrl = GlobalVariables.baseUrl + '/index.php/installation/ajax_install',
+ postData = {
+ csrfToken: GlobalVariables.csrfToken,
+ admin: JSON.stringify(getAdminData()),
+ company: JSON.stringify(getCompanyData())
+ };
$.ajax({
url: postUrl,
@@ -41,11 +45,9 @@ $(document).ready(function() {
data: postData,
datatype: 'json',
success: function(response) {
- //////////////////////////////////////////////////////
- console.log('Ajax Install E!A Response:', response);
- //////////////////////////////////////////////////////
-
- if (!GeneralFunctions.handleAjaxExceptions(response)) return;
+ if (!GeneralFunctions.handleAjaxExceptions(response)) {
+ return;
+ }
$('.alert').text('Easy!Appointments has been successfully installed!');
$('.alert').addClass('alert-success');
@@ -90,8 +92,9 @@ $(document).ready(function() {
}
});
- if (missingRequired)
+ if (missingRequired) {
throw 'All the page fields are required.';
+ }
// Validate Passwords
if ($('#password').val() != $('#retype-password').val()) {
@@ -132,12 +135,12 @@ $(document).ready(function() {
*/
function getAdminData() {
var admin = {
- 'first_name': $('#first-name').val(),
- 'last_name': $('#last-name').val(),
- 'email': $('#email').val(),
- 'phone_number': $('#phone-number').val(),
- 'username': $('#username').val(),
- 'password': $('#password').val()
+ first_name: $('#first-name').val(),
+ last_name: $('#last-name').val(),
+ email: $('#email').val(),
+ phone_number: $('#phone-number').val(),
+ username: $('#username').val(),
+ password: $('#password').val()
};
return admin;
@@ -150,9 +153,9 @@ $(document).ready(function() {
*/
function getCompanyData() {
var company = {
- 'company_name': $('#company-name').val(),
- 'company_email': $('#company-email').val(),
- 'company_link': $('#company-link').val()
+ company_name: $('#company-name').val(),
+ company_email: $('#company-email').val(),
+ company_link: $('#company-link').val()
};
return company;
diff --git a/src/assets/js/working_plan.js b/src/assets/js/working_plan.js
index 8f6d5099..4afb4a55 100644
--- a/src/assets/js/working_plan.js
+++ b/src/assets/js/working_plan.js
@@ -9,413 +9,431 @@
* @since v1.0.0
* ---------------------------------------------------------------------------- */
-/**
- * Contains the working plan functionality. The working plan DOM elements must be same
- * in every page this class is used.
- *
- * @class WorkingPlan
- */
-var WorkingPlan = function() {
- /**
- * This flag is used when trying to cancel row editing. It is
- * true only whenever the user presses the cancel button.
- *
- * @type {bool}
- */
- this.enableCancel = false;
+(function() {
+
+ 'use strict';
/**
- * This flag determines whether the jeditables are allowed to submit. It is
- * true only whenever the user presses the save button.
+ * Contains the working plan functionality. The working plan DOM elements must be same
+ * in every page this class is used.
*
- * @type {bool}
+ * @class WorkingPlan
*/
- this.enableSubmit = false;
-};
+ var WorkingPlan = function() {
+ /**
+ * This flag is used when trying to cancel row editing. It is
+ * true only whenever the user presses the cancel button.
+ *
+ * @type {bool}
+ */
+ this.enableCancel = false;
-/**
- * Setup the dom elements of a given working plan.
- *
- * @param {object} workingPlan Contains the working hours and breaks for each day of the week.
- */
-WorkingPlan.prototype.setup = function(workingPlan) {
- $.each(workingPlan, function(index, workingDay) {
- if (workingDay != null) {
- $('#' + index).prop('checked', true);
- $('#' + index + '-start').val(workingDay.start);
- $('#' + index + '-end').val(workingDay.end);
-
- // Add the day's breaks on the breaks table.
- $.each(workingDay.breaks, function(i, brk) {
- var day = WorkingPlan.prototype.convertValueToDay(index);
-
- var tr =
- '' +
- '' + GeneralFunctions.ucaseFirstLetter(day) + ' | ' +
- '' + brk.start + ' | ' +
- '' + brk.end + ' | ' +
- '' +
- '' +
- '' +
- '' +
- '' +
- ' | ' +
- '
';
- $('.breaks').append(tr);
- });
- } else {
- $('#' + index).prop('checked', false);
- $('#' + index + '-start').prop('disabled', true);
- $('#' + index + '-end').prop('disabled', true);
- }
- });
-
- // Make break cells editable.
- WorkingPlan.prototype.editableBreakDay($('.breaks .break-day'));
- WorkingPlan.prototype.editableBreakTime($('.breaks').find('.break-start, .break-end'));
-};
-
-/**
- * This method makes editable the break day cells.
- *
- * @param {object} $selector The jquery selector ready for use.
- */
-WorkingPlan.prototype.editableBreakDay = function($selector) {
- var weekDays = {};
- weekDays[EALang['monday']] = EALang['monday']; //'Monday';
- weekDays[EALang['tuesday']] = EALang['tuesday']; //'Tuesday';
- weekDays[EALang['wednesday']] = EALang['wednesday']; //'Wednesday';
- weekDays[EALang['thursday']] = EALang['thursday']; //'Thursday';
- weekDays[EALang['friday']] = EALang['friday']; //'Friday';
- weekDays[EALang['saturday']] = EALang['saturday']; //'Saturday';
- weekDays[EALang['sunday']] = EALang['sunday']; //'Sunday';
-
- $selector.editable(function(value, settings) {
- return value;
- }, {
- 'type': 'select',
- 'data': weekDays,
- 'event': 'edit',
- 'height': '30px',
- 'submit': '',
- 'cancel': '',
- 'onblur': 'ignore',
- 'onreset': function(settings, td) {
- if (!WorkingPlan.prototype.enableCancel) return false; // disable ESC button
- },
- 'onsubmit': function(settings, td) {
- if (!WorkingPlan.prototype.enableSubmit) return false; // disable Enter button
- }
- });
-};
-
-/**
- * This method makes editable the break time cells.
- *
- * @param {object} $selector The jquery selector ready for use.
- */
-WorkingPlan.prototype.editableBreakTime = function($selector) {
- $selector.editable(function(value, settings) {
- // Do not return the value because the user needs to press the "Save" button.
- return value;
- }, {
- 'event': 'edit',
- 'height': '25px',
- 'submit': '',
- 'cancel': '',
- 'onblur': 'ignore',
- 'onreset': function(settings, td) {
- if (!WorkingPlan.prototype.enableCancel) return false; // disable ESC button
- },
- 'onsubmit': function(settings, td) {
- if (!WorkingPlan.prototype.enableSubmit) return false; // disable Enter button
- }
- });
-};
-
-/**
- * Binds the event handlers for the working plan dom elements.
- */
-WorkingPlan.prototype.bindEventHandlers = function() {
- /**
- * Event: Day Checkbox "Click"
- *
- * Enable or disable the time selection for each day.
- */
- $('.working-plan input[type="checkbox"]').click(function() {
- var id = $(this).attr('id');
-
- if ($(this).prop('checked') == true) {
- $('#' + id + '-start').prop('disabled', false).val('09:00');
- $('#' + id + '-end').prop('disabled', false).val('18:00');
- } else {
- $('#' + id + '-start').prop('disabled', true).val('');
- $('#' + id + '-end').prop('disabled', true).val('');
- }
- });
+ /**
+ * This flag determines whether the jeditables are allowed to submit. It is
+ * true only whenever the user presses the save button.
+ *
+ * @type {bool}
+ */
+ this.enableSubmit = false;
+ };
/**
- * Event: Add Break Button "Click"
+ * Setup the dom elements of a given working plan.
*
- * A new row is added on the table and the user can enter the new break
- * data. After that he can either press the save or cancel button.
+ * @param {object} workingPlan Contains the working hours and breaks for each day of the week.
*/
- $('.add-break').click(function() {
- var tr =
- '' +
- '' + EALang['monday'] + ' | ' +
- '09:00 | ' +
- '10:00 | ' +
- '' +
- '' +
- '' +
- '' +
- '' +
- ' | ' +
- '
';
- $('.breaks').prepend(tr);
+ WorkingPlan.prototype.setup = function(workingPlan) {
+ $.each(workingPlan, function(index, workingDay) {
+ if (workingDay != null) {
+ $('#' + index).prop('checked', true);
+ $('#' + index + '-start').val(workingDay.start);
+ $('#' + index + '-end').val(workingDay.end);
- // Bind editable and event handlers.
- tr = $('.breaks tr').get()[1];
- WorkingPlan.prototype.editableBreakDay($(tr).find('.break-day'));
- WorkingPlan.prototype.editableBreakTime($(tr).find('.break-start, .break-end'));
- $(tr).find('.edit-break').trigger('click');
- $('.add-break').prop('disabled', true);
- });
+ // Add the day's breaks on the breaks table.
+ $.each(workingDay.breaks, function(i, brk) {
+ var day = this.convertValueToDay(index);
+
+ var tr =
+ '' +
+ '' + GeneralFunctions.ucaseFirstLetter(day) + ' | ' +
+ '' + brk.start + ' | ' +
+ '' + brk.end + ' | ' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ ' | ' +
+ '
';
+ $('.breaks').append(tr);
+ }.bind(this));
+ } else {
+ $('#' + index).prop('checked', false);
+ $('#' + index + '-start').prop('disabled', true);
+ $('#' + index + '-end').prop('disabled', true);
+ }
+ }.bind(this));
+
+ // Make break cells editable.
+ this.editableBreakDay($('.breaks .break-day'));
+ this.editableBreakTime($('.breaks').find('.break-start, .break-end'));
+ };
/**
- * Event: Edit Break Button "Click"
+ * This method makes editable the break day cells.
*
- * Enables the row editing for the "Breaks" table rows.
+ * @param {object} $selector The jquery selector ready for use.
*/
- $(document).on('click', '.edit-break', function() {
- // Reset previous editable tds
- var $previousEdt = $(this).closest('table').find('.editable').get();
- $.each($previousEdt, function(index, edt) {
- if (edt.reset !== undefined) edt.reset();
- });
+ WorkingPlan.prototype.editableBreakDay = function($selector) {
+ var weekDays = {};
+ weekDays[EALang['monday']] = EALang['monday']; //'Monday';
+ weekDays[EALang['tuesday']] = EALang['tuesday']; //'Tuesday';
+ weekDays[EALang['wednesday']] = EALang['wednesday']; //'Wednesday';
+ weekDays[EALang['thursday']] = EALang['thursday']; //'Thursday';
+ weekDays[EALang['friday']] = EALang['friday']; //'Friday';
+ weekDays[EALang['saturday']] = EALang['saturday']; //'Saturday';
+ weekDays[EALang['sunday']] = EALang['sunday']; //'Sunday';
- // Make all cells in current row editable.
- $(this).parent().parent().children().trigger('edit');
- $(this).parent().parent().find('.break-start input, .break-end input').timepicker({
- currentText: EALang['now'],
- closeText: EALang['close'],
- timeOnlyTitle: EALang['select_time'],
- timeText: EALang['time'],
- hourText: EALang['hour'],
- minuteText: EALang['minutes']
- });
- $(this).parent().parent().find('.break-day select').focus();
-
- // Show save - cancel buttons.
- $(this).closest('table').find('.edit-break, .delete-break').addClass('hidden');
- $(this).parent().find('.save-break, .cancel-break').removeClass('hidden');
-
- $('.add-break').prop('disabled', true);
- });
-
- /**
- * Event: Delete Break Button "Click"
- *
- * Removes the current line from the "Breaks" table.
- */
- $(document).on('click', '.delete-break', function() {
- $(this).parent().parent().remove();
- });
-
- /**
- * Event: Cancel Break Button "Click"
- *
- * Bring the ".breaks" table back to its initial state.
- */
- $(document).on('click', '.cancel-break', function() {
- WorkingPlan.prototype.enableCancel = true;
- $(this).parent().parent().find('.cancel-editable').trigger('click');
- WorkingPlan.prototype.enableCancel = false;
-
- $(this).closest('table').find('.edit-break, .delete-break').removeClass('hidden');
- $(this).parent().find('.save-break, .cancel-break').addClass('hidden');
- $('.add-break').prop('disabled', false);
- });
-
- /**
- * Event: Save Break Button "Click"
- *
- * Save the editable values and restore the table to its initial state.
- */
- $(document).on('click', '.save-break', function() {
- // Break's start time must always be prior to break's end.
- var start = Date.parse($(this).parent().parent().find('.break-start input').val());
- var end = Date.parse($(this).parent().parent().find('.break-end input').val());
- if (start > end) {
- $(this).parent().parent().find('.break-end input').val(start.addHours(1).toString('HH:mm'));
- }
-
- WorkingPlan.prototype.enableSubmit = true;
- $(this).parent().parent().find('.editable .submit-editable').trigger('click');
- WorkingPlan.prototype.enableSubmit = false;
-
- $(this).parent().find('.save-break, .cancel-break').addClass('hidden');
- $(this).closest('table').find('.edit-break, .delete-break').removeClass('hidden');
- $('.add-break').prop('disabled', false);
- });
-};
-
-/**
- * Get the working plan settings.
- *
- * @returns {object} Returns the working plan settings object.
- */
-WorkingPlan.prototype.get = function() {
- var workingPlan = {};
- $('.working-plan input[type="checkbox"]').each(function() {
- var id = $(this).attr('id');
- if ($(this).prop('checked') == true) {
- workingPlan[id] = {};
- workingPlan[id].start = $('#' + id + '-start').val();
- workingPlan[id].end = $('#' + id + '-end').val();
- workingPlan[id].breaks = [];
-
- $('.breaks tr').each(function(index, tr) {
- var day = WorkingPlan.prototype.convertDayToValue(
- $(tr).find('.break-day').text());
- if (day == id) {
- var start = $(tr).find('.break-start').text();
- var end = $(tr).find('.break-end').text();
-
- workingPlan[id].breaks.push({
- 'start': start,
- 'end': end
- });
+ $selector.editable(function(value, settings) {
+ return value;
+ }, {
+ type: 'select',
+ data: weekDays,
+ event: 'edit',
+ height: '30px',
+ submit: '',
+ cancel: '',
+ onblur: 'ignore',
+ onreset: function(settings, td) {
+ if (!this.enableCancel) {
+ return false; // disable ESC button
}
- });
-
- } else {
- workingPlan[id] = null;
- }
- });
-
- return workingPlan;
-};
-
-/**
- * Enables or disabled the timepicker functionality from the working plan input
- * text fields.
- *
- * @param {bool} disabled (OPTIONAL = false) If true then the timepickers will be
- * disabled.
- */
-WorkingPlan.prototype.timepickers = function(disabled) {
- if (disabled == undefined) disabled == false;
-
- if (disabled == false) {
- // Set timepickers where needed.
- $('.working-plan input[type="text"]').timepicker({
- 'timeFormat': 'HH:mm',
-
- currentText: EALang['now'],
- closeText: EALang['close'],
- timeOnlyTitle: EALang['select_time'],
- timeText: EALang['time'],
- hourText: EALang['hour'],
- minuteText: EALang['minutes'],
-
- 'onSelect': function(datetime, inst) {
- // Start time must be earlier than end time.
- var start = Date.parse($(this).parent().parent().find('.work-start').val());
- var end = Date.parse($(this).parent().parent().find('.work-end').val());
-
- if (start > end) {
- $(this).parent().parent().find('.work-end').val(start.addHours(1).toString('HH:mm'));
+ }.bind(this),
+ onsubmit: function(settings, td) {
+ if (!this.enableSubmit) {
+ return false; // disable Enter button
}
+ }.bind(this)
+ });
+ };
+
+ /**
+ * This method makes editable the break time cells.
+ *
+ * @param {object} $selector The jquery selector ready for use.
+ */
+ WorkingPlan.prototype.editableBreakTime = function($selector) {
+ $selector.editable(function(value, settings) {
+ // Do not return the value because the user needs to press the "Save" button.
+ return value;
+ }, {
+ event: 'edit',
+ height: '25px',
+ submit: '',
+ cancel: '',
+ onblur: 'ignore',
+ onreset: function(settings, td) {
+ if (!this.enableCancel) {
+ return false; // disable ESC button
+ }
+ }.bind(this),
+ onsubmit: function(settings, td) {
+ if (!this.enableSubmit) {
+ return false; // disable Enter button
+ }
+ }.bind(this)
+ });
+ };
+
+ /**
+ * Binds the event handlers for the working plan dom elements.
+ */
+ WorkingPlan.prototype.bindEventHandlers = function() {
+ /**
+ * Event: Day Checkbox "Click"
+ *
+ * Enable or disable the time selection for each day.
+ */
+ $('.working-plan input[type="checkbox"]').click(function() {
+ var id = $(this).attr('id');
+
+ if ($(this).prop('checked') == true) {
+ $('#' + id + '-start').prop('disabled', false).val('09:00');
+ $('#' + id + '-end').prop('disabled', false).val('18:00');
+ } else {
+ $('#' + id + '-start').prop('disabled', true).val('');
+ $('#' + id + '-end').prop('disabled', true).val('');
}
});
- } else {
- $('.working-plan input').timepicker('destroy');
- }
-};
-/**
- * Reset the current plan back to the company's default working plan.
- */
-WorkingPlan.prototype.reset = function() {
+ /**
+ * Event: Add Break Button "Click"
+ *
+ * A new row is added on the table and the user can enter the new break
+ * data. After that he can either press the save or cancel button.
+ */
+ $('.add-break').click(function() {
+ var tr =
+ '' +
+ '' + EALang['monday'] + ' | ' +
+ '09:00 | ' +
+ '10:00 | ' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ ' | ' +
+ '
';
+ $('.breaks').prepend(tr);
-};
+ // Bind editable and event handlers.
+ tr = $('.breaks tr')[1];
+ this.editableBreakDay($(tr).find('.break-day'));
+ this.editableBreakTime($(tr).find('.break-start, .break-end'));
+ $(tr).find('.edit-break').trigger('click');
+ $('.add-break').prop('disabled', true);
+ }.bind(this));
-/**
- * This is necessary for translated days.
- *
- * @param {string} value Day value could be like "monday", "tuesday" etc.
- */
-WorkingPlan.prototype.convertValueToDay = function(value) {
- switch (value) {
- case 'monday':
- return EALang['monday'];
- break;
- case 'tuesday':
- return EALang['tuesday'];
- break;
- case 'wednesday':
- return EALang['wednesday'];
- break;
- case 'thursday':
- return EALang['thursday'];
- break;
- case 'friday':
- return EALang['friday'];
- break;
- case 'saturday':
- return EALang['saturday'];
- break;
- case 'sunday':
- return EALang['sunday'];
- break;
- }
-};
+ /**
+ * Event: Edit Break Button "Click"
+ *
+ * Enables the row editing for the "Breaks" table rows.
+ */
+ $(document).on('click', '.edit-break', function() {
+ // Reset previous editable tds
+ var $previousEdt = $(this).closest('table').find('.editable').get();
+ $.each($previousEdt, function(index, edt) {
+ if (edt.reset !== undefined) {
+ edt.reset();
+ }
+ });
-/**
- * This is necessary for translated days.
- *
- * @param {string} value Day value could be like "Monday", "Tuesday" etc.
- */
-WorkingPlan.prototype.convertDayToValue = function(day) {
- switch (day) {
- case EALang['monday']:
- return 'monday';
- break;
- case EALang['tuesday']:
- return 'tuesday';
- break;
- case EALang['wednesday']:
- return 'wednesday';
- break;
- case EALang['thursday']:
- return 'thursday';
- break;
- case EALang['friday']:
- return 'friday';
- break;
- case EALang['saturday']:
- return 'saturday';
- break;
- case EALang['sunday']:
- return 'sunday';
- break;
- }
-};
+ // Make all cells in current row editable.
+ $(this).parent().parent().children().trigger('edit');
+ $(this).parent().parent().find('.break-start input, .break-end input').timepicker({
+ currentText: EALang['now'],
+ closeText: EALang['close'],
+ timeOnlyTitle: EALang['select_time'],
+ timeText: EALang['time'],
+ hourText: EALang['hour'],
+ minuteText: EALang['minutes']
+ });
+ $(this).parent().parent().find('.break-day select').focus();
+
+ // Show save - cancel buttons.
+ $(this).closest('table').find('.edit-break, .delete-break').addClass('hidden');
+ $(this).parent().find('.save-break, .cancel-break').removeClass('hidden');
+
+ $('.add-break').prop('disabled', true);
+ });
+
+ /**
+ * Event: Delete Break Button "Click"
+ *
+ * Removes the current line from the "Breaks" table.
+ */
+ $(document).on('click', '.delete-break', function() {
+ $(this).parent().parent().remove();
+ });
+
+ /**
+ * Event: Cancel Break Button "Click"
+ *
+ * Bring the ".breaks" table back to its initial state.
+ */
+ $(document).on('click', '.cancel-break', function(e) {
+ var element = e.target;
+ this.enableCancel = true;
+ $(element).parent().parent().find('.cancel-editable').trigger('click');
+ this.enableCancel = false;
+
+ $(element).closest('table').find('.edit-break, .delete-break').removeClass('hidden');
+ $(element).parent().find('.save-break, .cancel-break').addClass('hidden');
+ $('.add-break').prop('disabled', false);
+ }.bind(this));
+
+ /**
+ * Event: Save Break Button "Click"
+ *
+ * Save the editable values and restore the table to its initial state.
+ */
+ $(document).on('click', '.save-break', function(e) {
+ // Break's start time must always be prior to break's end.
+ var element = e.target,
+ start = Date.parse($(element).parent().parent().find('.break-start input').val()),
+ end = Date.parse($(element).parent().parent().find('.break-end input').val());
+ if (start > end) {
+ $(element).parent().parent().find('.break-end input').val(start.addHours(1).toString('HH:mm'));
+ }
+
+ this.enableSubmit = true;
+ $(element).parent().parent().find('.editable .submit-editable').trigger('click');
+ this.enableSubmit = false;
+
+ $(element).parent().find('.save-break, .cancel-break').addClass('hidden');
+ $(element).closest('table').find('.edit-break, .delete-break').removeClass('hidden');
+ $('.add-break').prop('disabled', false);
+ }.bind(this));
+ };
+
+ /**
+ * Get the working plan settings.
+ *
+ * @returns {object} Returns the working plan settings object.
+ */
+ WorkingPlan.prototype.get = function() {
+ var workingPlan = {};
+ $('.working-plan input[type="checkbox"]').each(function(index, checkbox) {
+ var id = $(checkbox).attr('id');
+ if ($(checkbox).prop('checked') == true) {
+ workingPlan[id] = {};
+ workingPlan[id].start = $('#' + id + '-start').val();
+ workingPlan[id].end = $('#' + id + '-end').val();
+ workingPlan[id].breaks = [];
+
+ $('.breaks tr').each(function(index, tr) {
+ var day = this.convertDayToValue($(tr).find('.break-day').text());
+ if (day == id) {
+ var start = $(tr).find('.break-start').text(),
+ end = $(tr).find('.break-end').text();
+
+ workingPlan[id].breaks.push({
+ 'start': start,
+ 'end': end
+ });
+ }
+ }.bind(this));
+ } else {
+ workingPlan[id] = null;
+ }
+ }.bind(this));
+
+ return workingPlan;
+ };
+
+ /**
+ * Enables or disabled the timepicker functionality from the working plan input
+ * text fields.
+ *
+ * @param {bool} disabled (OPTIONAL = false) If true then the timepickers will be
+ * disabled.
+ */
+ WorkingPlan.prototype.timepickers = function(disabled) {
+ disabled = disabled || false;
+
+ if (disabled == false) {
+ // Set timepickers where needed.
+ $('.working-plan input[type="text"]').timepicker({
+ timeFormat: 'HH:mm',
+
+ currentText: EALang['now'],
+ closeText: EALang['close'],
+ timeOnlyTitle: EALang['select_time'],
+ timeText: EALang['time'],
+ hourText: EALang['hour'],
+ minuteText: EALang['minutes'],
+
+ onSelect: function(datetime, inst) {
+ // Start time must be earlier than end time.
+ var start = Date.parse($(this).parent().parent().find('.work-start').val()),
+ end = Date.parse($(this).parent().parent().find('.work-end').val());
+
+ if (start > end) {
+ $(this).parent().parent().find('.work-end').val(start.addHours(1).toString('HH:mm'));
+ }
+ }
+ });
+ } else {
+ $('.working-plan input').timepicker('destroy');
+ }
+ };
+
+ /**
+ * Reset the current plan back to the company's default working plan.
+ */
+ WorkingPlan.prototype.reset = function() {
+
+ };
+
+ /**
+ * This is necessary for translated days.
+ *
+ * @param {string} value Day value could be like "monday", "tuesday" etc.
+ */
+ WorkingPlan.prototype.convertValueToDay = function(value) {
+ switch (value) {
+ case 'monday':
+ return EALang['monday'];
+ break;
+ case 'tuesday':
+ return EALang['tuesday'];
+ break;
+ case 'wednesday':
+ return EALang['wednesday'];
+ break;
+ case 'thursday':
+ return EALang['thursday'];
+ break;
+ case 'friday':
+ return EALang['friday'];
+ break;
+ case 'saturday':
+ return EALang['saturday'];
+ break;
+ case 'sunday':
+ return EALang['sunday'];
+ break;
+ }
+ };
+
+ /**
+ * This is necessary for translated days.
+ *
+ * @param {string} value Day value could be like "Monday", "Tuesday" etc.
+ */
+ WorkingPlan.prototype.convertDayToValue = function(day) {
+ switch (day) {
+ case EALang['monday']:
+ return 'monday';
+ break;
+ case EALang['tuesday']:
+ return 'tuesday';
+ break;
+ case EALang['wednesday']:
+ return 'wednesday';
+ break;
+ case EALang['thursday']:
+ return 'thursday';
+ break;
+ case EALang['friday']:
+ return 'friday';
+ break;
+ case EALang['saturday']:
+ return 'saturday';
+ break;
+ case EALang['sunday']:
+ return 'sunday';
+ break;
+ }
+ };
+
+ window.WorkingPlan = WorkingPlan;
+
+})();
diff --git a/src/index.php b/src/index.php
index 6d79f162..fc186491 100644
--- a/src/index.php
+++ b/src/index.php
@@ -1,31 +1,66 @@
='))
+ {
+ error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT & ~E_USER_NOTICE & ~E_USER_DEPRECATED);
+ }
+ else
+ {
+ error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT & ~E_USER_NOTICE);
+ }
+ break;
- default:
- exit('The application environment is not set correctly.');
- }
+ default:
+ header('HTTP/1.1 503 Service Unavailable.', TRUE, 503);
+ echo 'The application environment is not set correctly.';
+ exit(1); // EXIT_ERROR
}
/*
*---------------------------------------------------------------
- * SYSTEM FOLDER NAME
+ * SYSTEM DIRECTORY NAME
*---------------------------------------------------------------
*
- * This variable must contain the name of your "system" folder.
- * Include the path if the folder is not in the same directory
- * as this file.
- *
+ * This variable must contain the name of your "system" directory.
+ * Set the path if it is not in the same directory as this file.
*/
$system_path = 'system';
/*
*---------------------------------------------------------------
- * APPLICATION FOLDER NAME
+ * APPLICATION DIRECTORY NAME
*---------------------------------------------------------------
*
* If you want this front controller to use a different "application"
- * folder then the default one you can set its name here. The folder
- * can also be renamed or relocated anywhere on your server. If
- * you do, use a full server path. For more info please see the user guide:
- * http://codeigniter.com/user_guide/general/managing_apps.html
+ * directory than the default one you can set its name here. The directory
+ * can also be renamed or relocated anywhere on your server. If you do,
+ * use an absolute (full) server path.
+ * For more info please see the user guide:
+ *
+ * https://codeigniter.com/user_guide/general/managing_apps.html
*
* NO TRAILING SLASH!
- *
*/
$application_folder = 'application';
+/*
+ *---------------------------------------------------------------
+ * VIEW DIRECTORY NAME
+ *---------------------------------------------------------------
+ *
+ * If you want to move the view directory out of the application
+ * directory, set the path to it here. The directory can be renamed
+ * and relocated anywhere on your server. If blank, it will default
+ * to the standard location inside your application directory.
+ * If you do move this, use an absolute (full) server path.
+ *
+ * NO TRAILING SLASH!
+ */
+ $view_folder = '';
+
+
/*
* --------------------------------------------------------------------
* DEFAULT CONTROLLER
@@ -108,24 +165,23 @@ if (defined('ENVIRONMENT'))
*
* Normally you will set your default controller in the routes.php file.
* You can, however, force a custom routing by hard-coding a
- * specific controller class/function here. For most applications, you
+ * specific controller class/function here. For most applications, you
* WILL NOT set your routing here, but it's an option for those
* special instances where you might want to override the standard
* routing in a specific front controller that shares a common CI installation.
*
- * IMPORTANT: If you set the routing here, NO OTHER controller will be
+ * IMPORTANT: If you set the routing here, NO OTHER controller will be
* callable. In essence, this preference limits your application to ONE
- * specific controller. Leave the function name blank if you need
+ * specific controller. Leave the function name blank if you need
* to call functions dynamically via the URI.
*
* Un-comment the $routing array below to use this feature
- *
*/
- // The directory name, relative to the "controllers" folder. Leave blank
- // if your controller is not in a sub-folder within the "controllers" folder
+ // The directory name, relative to the "controllers" directory. Leave blank
+ // if your controller is not in a sub-directory within the "controllers" one
// $routing['directory'] = '';
- // The controller class file name. Example: Mycontroller
+ // The controller class file name. Example: mycontroller
// $routing['controller'] = '';
// The controller function you wish to be called.
@@ -145,7 +201,6 @@ if (defined('ENVIRONMENT'))
* config values.
*
* Un-comment the $assign_to_config array below to use this feature
- *
*/
// $assign_to_config['name_of_config_item'] = 'value of config item';
@@ -167,18 +222,26 @@ if (defined('ENVIRONMENT'))
chdir(dirname(__FILE__));
}
- if (realpath($system_path) !== FALSE)
+ if (($_temp = realpath($system_path)) !== FALSE)
{
- $system_path = realpath($system_path).'/';
+ $system_path = $_temp.DIRECTORY_SEPARATOR;
+ }
+ else
+ {
+ // Ensure there's a trailing slash
+ $system_path = strtr(
+ rtrim($system_path, '/\\'),
+ '/\\',
+ DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR
+ ).DIRECTORY_SEPARATOR;
}
-
- // ensure there's a trailing slash
- $system_path = rtrim($system_path, '/').'/';
// Is the system path correct?
if ( ! is_dir($system_path))
{
- exit("Your system folder path does not appear to be set correctly. Please open the following file and correct this: ".pathinfo(__FILE__, PATHINFO_BASENAME));
+ header('HTTP/1.1 503 Service Unavailable.', TRUE, 503);
+ echo 'Your system folder path does not appear to be set correctly. Please open the following file and correct this: '.pathinfo(__FILE__, PATHINFO_BASENAME);
+ exit(3); // EXIT_CONFIG
}
/*
@@ -189,44 +252,90 @@ if (defined('ENVIRONMENT'))
// The name of THIS file
define('SELF', pathinfo(__FILE__, PATHINFO_BASENAME));
- // The PHP file extension
- // this global constant is deprecated.
- define('EXT', '.php');
+ // Path to the system directory
+ define('BASEPATH', $system_path);
- // Path to the system folder
- define('BASEPATH', str_replace("\\", "/", $system_path));
+ // Path to the front controller (this file) directory
+ define('FCPATH', dirname(__FILE__).DIRECTORY_SEPARATOR);
- // Path to the front controller (this file)
- define('FCPATH', str_replace(SELF, '', __FILE__));
+ // Name of the "system" directory
+ define('SYSDIR', basename(BASEPATH));
- // Name of the "system folder"
- define('SYSDIR', trim(strrchr(trim(BASEPATH, '/'), '/'), '/'));
-
-
- // The path to the "application" folder
+ // The path to the "application" directory
if (is_dir($application_folder))
{
- define('APPPATH', $application_folder.'/');
+ if (($_temp = realpath($application_folder)) !== FALSE)
+ {
+ $application_folder = $_temp;
+ }
+ else
+ {
+ $application_folder = strtr(
+ rtrim($application_folder, '/\\'),
+ '/\\',
+ DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR
+ );
+ }
+ }
+ elseif (is_dir(BASEPATH.$application_folder.DIRECTORY_SEPARATOR))
+ {
+ $application_folder = BASEPATH.strtr(
+ trim($application_folder, '/\\'),
+ '/\\',
+ DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR
+ );
}
else
{
- if ( ! is_dir(BASEPATH.$application_folder.'/'))
- {
- exit("Your application folder path does not appear to be set correctly. Please open the following file and correct this: ".SELF);
- }
-
- define('APPPATH', BASEPATH.$application_folder.'/');
+ header('HTTP/1.1 503 Service Unavailable.', TRUE, 503);
+ echo 'Your application folder path does not appear to be set correctly. Please open the following file and correct this: '.SELF;
+ exit(3); // EXIT_CONFIG
}
+ define('APPPATH', $application_folder.DIRECTORY_SEPARATOR);
+
+ // The path to the "views" directory
+ if ( ! isset($view_folder[0]) && is_dir(APPPATH.'views'.DIRECTORY_SEPARATOR))
+ {
+ $view_folder = APPPATH.'views';
+ }
+ elseif (is_dir($view_folder))
+ {
+ if (($_temp = realpath($view_folder)) !== FALSE)
+ {
+ $view_folder = $_temp;
+ }
+ else
+ {
+ $view_folder = strtr(
+ rtrim($view_folder, '/\\'),
+ '/\\',
+ DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR
+ );
+ }
+ }
+ elseif (is_dir(APPPATH.$view_folder.DIRECTORY_SEPARATOR))
+ {
+ $view_folder = APPPATH.strtr(
+ trim($view_folder, '/\\'),
+ '/\\',
+ DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR
+ );
+ }
+ else
+ {
+ header('HTTP/1.1 503 Service Unavailable.', TRUE, 503);
+ echo 'Your view folder path does not appear to be set correctly. Please open the following file and correct this: '.SELF;
+ exit(3); // EXIT_CONFIG
+ }
+
+ define('VIEWPATH', $view_folder.DIRECTORY_SEPARATOR);
+
/*
* --------------------------------------------------------------------
* LOAD THE BOOTSTRAP FILE
* --------------------------------------------------------------------
*
* And away we go...
- *
*/
require_once BASEPATH.'core/CodeIgniter.php';
-
-/* End of file index.php */
-/* Location: ./index.php */
diff --git a/src/system/.htaccess b/src/system/.htaccess
index 14249c50..97c65d2d 100644
--- a/src/system/.htaccess
+++ b/src/system/.htaccess
@@ -1 +1,6 @@
-Deny from all
\ No newline at end of file
+
+ Require all denied
+
+
+ Deny from all
+
\ No newline at end of file
diff --git a/src/system/core/Benchmark.php b/src/system/core/Benchmark.php
index daffa292..b1d74f78 100644
--- a/src/system/core/Benchmark.php
+++ b/src/system/core/Benchmark.php
@@ -1,62 +1,82 @@
-marker[$name] = microtime();
+ $this->marker[$name] = microtime(TRUE);
}
// --------------------------------------------------------------------
/**
+ * Elapsed time
+ *
* Calculates the time difference between two marked points.
*
* If the first parameter is empty this function instead returns the
@@ -64,15 +84,17 @@ class CI_Benchmark {
* execution time to be shown in a template. The output class will
* swap the real value for this variable.
*
- * @access public
- * @param string a particular marked point
- * @param string a particular marked point
- * @param integer the number of decimal places
- * @return mixed
+ * @param string $point1 A particular marked point
+ * @param string $point2 A particular marked point
+ * @param int $decimals Number of decimal places
+ *
+ * @return string Calculated elapsed time on success,
+ * an '{elapsed_string}' if $point1 is empty
+ * or an empty string if $point1 is not found.
*/
- function elapsed_time($point1 = '', $point2 = '', $decimals = 4)
+ public function elapsed_time($point1 = '', $point2 = '', $decimals = 4)
{
- if ($point1 == '')
+ if ($point1 === '')
{
return '{elapsed_time}';
}
@@ -84,13 +106,10 @@ class CI_Benchmark {
if ( ! isset($this->marker[$point2]))
{
- $this->marker[$point2] = microtime();
+ $this->marker[$point2] = microtime(TRUE);
}
- list($sm, $ss) = explode(' ', $this->marker[$point1]);
- list($em, $es) = explode(' ', $this->marker[$point2]);
-
- return number_format(($em + $es) - ($sm + $ss), $decimals);
+ return number_format($this->marker[$point2] - $this->marker[$point1], $decimals);
}
// --------------------------------------------------------------------
@@ -98,22 +117,17 @@ class CI_Benchmark {
/**
* Memory Usage
*
- * This function returns the {memory_usage} pseudo-variable.
+ * Simply returns the {memory_usage} marker.
+ *
* This permits it to be put it anywhere in a template
* without the memory being calculated until the end.
* The output class will swap the real value for this variable.
*
- * @access public
- * @return string
+ * @return string '{memory_usage}'
*/
- function memory_usage()
+ public function memory_usage()
{
return '{memory_usage}';
}
}
-
-// END CI_Benchmark class
-
-/* End of file Benchmark.php */
-/* Location: ./system/core/Benchmark.php */
\ No newline at end of file
diff --git a/src/system/core/CodeIgniter.php b/src/system/core/CodeIgniter.php
index 9f2e7510..3eb3e057 100644
--- a/src/system/core/CodeIgniter.php
+++ b/src/system/core/CodeIgniter.php
@@ -1,20 +1,41 @@
- '_ENV', 'G' => '_GET', 'P' => '_POST', 'C' => '_COOKIE', 'S' => '_SERVER') as $key => $superglobal)
+ {
+ if (strpos($_registered, $key) === FALSE)
+ {
+ continue;
+ }
+
+ foreach (array_keys($$superglobal) as $var)
+ {
+ if (isset($GLOBALS[$var]) && ! in_array($var, $_protected, TRUE))
+ {
+ $GLOBALS[$var] = NULL;
+ }
+ }
+ }
}
+}
+
/*
* ------------------------------------------------------
* Define a custom error handler so we can log PHP errors
* ------------------------------------------------------
*/
- set_error_handler('_exception_handler');
-
- if ( ! is_php('5.3'))
- {
- @set_magic_quotes_runtime(0); // Kill magic quotes
- }
+ set_error_handler('_error_handler');
+ set_exception_handler('_exception_handler');
+ register_shutdown_function('_shutdown_handler');
/*
* ------------------------------------------------------
@@ -86,26 +145,39 @@
* The subclass prefix allows CI to know if a core class is
* being extended via a library in the local application
* "libraries" folder. Since CI allows config items to be
- * overriden via data set in the main index. php file,
+ * overridden via data set in the main index.php file,
* before proceeding we need to know if a subclass_prefix
- * override exists. If so, we will set this value now,
+ * override exists. If so, we will set this value now,
* before any classes are loaded
* Note: Since the config file data is cached it doesn't
* hurt to load it here.
*/
- if (isset($assign_to_config['subclass_prefix']) AND $assign_to_config['subclass_prefix'] != '')
+ if ( ! empty($assign_to_config['subclass_prefix']))
{
get_config(array('subclass_prefix' => $assign_to_config['subclass_prefix']));
}
/*
* ------------------------------------------------------
- * Set a liberal script execution time limit
+ * Should we use a Composer autoloader?
* ------------------------------------------------------
*/
- if (function_exists("set_time_limit") == TRUE AND @ini_get("safe_mode") == 0)
+ if ($composer_autoload = config_item('composer_autoload'))
{
- @set_time_limit(300);
+ if ($composer_autoload === TRUE)
+ {
+ file_exists(APPPATH.'vendor/autoload.php')
+ ? require_once(APPPATH.'vendor/autoload.php')
+ : log_message('error', '$config[\'composer_autoload\'] is set to TRUE but '.APPPATH.'vendor/autoload.php was not found.');
+ }
+ elseif (file_exists($composer_autoload))
+ {
+ require_once($composer_autoload);
+ }
+ else
+ {
+ log_message('error', 'Could not find the specified $config[\'composer_autoload\'] path: '.$composer_autoload);
+ }
}
/*
@@ -129,33 +201,96 @@
* Is there a "pre_system" hook?
* ------------------------------------------------------
*/
- $EXT->_call_hook('pre_system');
+ $EXT->call_hook('pre_system');
/*
* ------------------------------------------------------
* Instantiate the config class
* ------------------------------------------------------
+ *
+ * Note: It is important that Config is loaded first as
+ * most other classes depend on it either directly or by
+ * depending on another class that uses it.
+ *
*/
$CFG =& load_class('Config', 'core');
// Do we have any manually set config items in the index.php file?
- if (isset($assign_to_config))
+ if (isset($assign_to_config) && is_array($assign_to_config))
{
- $CFG->_assign_to_config($assign_to_config);
+ foreach ($assign_to_config as $key => $value)
+ {
+ $CFG->set_item($key, $value);
+ }
}
+/*
+ * ------------------------------------------------------
+ * Important charset-related stuff
+ * ------------------------------------------------------
+ *
+ * Configure mbstring and/or iconv if they are enabled
+ * and set MB_ENABLED and ICONV_ENABLED constants, so
+ * that we don't repeatedly do extension_loaded() or
+ * function_exists() calls.
+ *
+ * Note: UTF-8 class depends on this. It used to be done
+ * in it's constructor, but it's _not_ class-specific.
+ *
+ */
+ $charset = strtoupper(config_item('charset'));
+ ini_set('default_charset', $charset);
+
+ if (extension_loaded('mbstring'))
+ {
+ define('MB_ENABLED', TRUE);
+ // mbstring.internal_encoding is deprecated starting with PHP 5.6
+ // and it's usage triggers E_DEPRECATED messages.
+ @ini_set('mbstring.internal_encoding', $charset);
+ // This is required for mb_convert_encoding() to strip invalid characters.
+ // That's utilized by CI_Utf8, but it's also done for consistency with iconv.
+ mb_substitute_character('none');
+ }
+ else
+ {
+ define('MB_ENABLED', FALSE);
+ }
+
+ // There's an ICONV_IMPL constant, but the PHP manual says that using
+ // iconv's predefined constants is "strongly discouraged".
+ if (extension_loaded('iconv'))
+ {
+ define('ICONV_ENABLED', TRUE);
+ // iconv.internal_encoding is deprecated starting with PHP 5.6
+ // and it's usage triggers E_DEPRECATED messages.
+ @ini_set('iconv.internal_encoding', $charset);
+ }
+ else
+ {
+ define('ICONV_ENABLED', FALSE);
+ }
+
+ if (is_php('5.6'))
+ {
+ ini_set('php.internal_encoding', $charset);
+ }
+
+/*
+ * ------------------------------------------------------
+ * Load compatibility features
+ * ------------------------------------------------------
+ */
+
+ require_once(BASEPATH.'core/compat/mbstring.php');
+ require_once(BASEPATH.'core/compat/hash.php');
+ require_once(BASEPATH.'core/compat/password.php');
+ require_once(BASEPATH.'core/compat/standard.php');
+
/*
* ------------------------------------------------------
* Instantiate the UTF-8 class
* ------------------------------------------------------
- *
- * Note: Order here is rather important as the UTF-8
- * class needs to be used very early on, but it cannot
- * properly determine if UTf-8 can be supported until
- * after the Config class is instantiated.
- *
*/
-
$UNI =& load_class('Utf8', 'core');
/*
@@ -170,14 +305,7 @@
* Instantiate the routing class and set the routing
* ------------------------------------------------------
*/
- $RTR =& load_class('Router', 'core');
- $RTR->_set_routing();
-
- // Set any routing overrides that may exist in the main index file
- if (isset($routing))
- {
- $RTR->_set_overrides($routing);
- }
+ $RTR =& load_class('Router', 'core', isset($routing) ? $routing : NULL);
/*
* ------------------------------------------------------
@@ -188,15 +316,12 @@
/*
* ------------------------------------------------------
- * Is there a valid cache file? If so, we're done...
+ * Is there a valid cache file? If so, we're done...
* ------------------------------------------------------
*/
- if ($EXT->_call_hook('cache_override') === FALSE)
+ if ($EXT->call_hook('cache_override') === FALSE && $OUT->_display_cache($CFG, $URI) === TRUE)
{
- if ($OUT->_display_cache($CFG, $URI) == TRUE)
- {
- exit;
- }
+ exit;
}
/*
@@ -227,76 +352,142 @@
*
*/
// Load the base controller class
- require BASEPATH.'core/Controller.php';
+ require_once BASEPATH.'core/Controller.php';
+ /**
+ * Reference to the CI_Controller method.
+ *
+ * Returns current CI instance object
+ *
+ * @return CI_Controller
+ */
function &get_instance()
{
return CI_Controller::get_instance();
}
-
if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'))
{
- require APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php';
+ require_once APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php';
}
- // Load the local application controller
- // Note: The Router class automatically validates the controller path using the router->_validate_request().
- // If this include fails it means that the default controller in the Routes.php file is not resolving to something valid.
- if ( ! file_exists(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php'))
- {
- show_error('Unable to load your default controller. Please make sure the controller specified in your Routes.php file is valid.');
- }
-
- include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php');
-
// Set a mark point for benchmarking
$BM->mark('loading_time:_base_classes_end');
/*
* ------------------------------------------------------
- * Security check
+ * Sanity checks
* ------------------------------------------------------
*
- * None of the functions in the app controller or the
- * loader class can be called via the URI, nor can
- * controller functions that begin with an underscore
+ * The Router class has already validated the request,
+ * leaving us with 3 options here:
+ *
+ * 1) an empty class name, if we reached the default
+ * controller, but it didn't exist;
+ * 2) a query string which doesn't go through a
+ * file_exists() check
+ * 3) a regular request for a non-existing page
+ *
+ * We handle all of these as a 404 error.
+ *
+ * Furthermore, none of the methods in the app controller
+ * or the loader class can be called via the URI, nor can
+ * controller methods that begin with an underscore.
*/
- $class = $RTR->fetch_class();
- $method = $RTR->fetch_method();
- if ( ! class_exists($class)
- OR strncmp($method, '_', 1) == 0
- OR in_array(strtolower($method), array_map('strtolower', get_class_methods('CI_Controller')))
- )
+ $e404 = FALSE;
+ $class = ucfirst($RTR->class);
+ $method = $RTR->method;
+
+ if (empty($class) OR ! file_exists(APPPATH.'controllers/'.$RTR->directory.$class.'.php'))
+ {
+ $e404 = TRUE;
+ }
+ else
+ {
+ require_once(APPPATH.'controllers/'.$RTR->directory.$class.'.php');
+
+ if ( ! class_exists($class, FALSE) OR $method[0] === '_' OR method_exists('CI_Controller', $method))
+ {
+ $e404 = TRUE;
+ }
+ elseif (method_exists($class, '_remap'))
+ {
+ $params = array($method, array_slice($URI->rsegments, 2));
+ $method = '_remap';
+ }
+ // WARNING: It appears that there are issues with is_callable() even in PHP 5.2!
+ // Furthermore, there are bug reports and feature/change requests related to it
+ // that make it unreliable to use in this context. Please, DO NOT change this
+ // work-around until a better alternative is available.
+ elseif ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($class)), TRUE))
+ {
+ $e404 = TRUE;
+ }
+ }
+
+ if ($e404)
{
if ( ! empty($RTR->routes['404_override']))
{
- $x = explode('/', $RTR->routes['404_override']);
- $class = $x[0];
- $method = (isset($x[1]) ? $x[1] : 'index');
- if ( ! class_exists($class))
+ if (sscanf($RTR->routes['404_override'], '%[^/]/%s', $error_class, $error_method) !== 2)
{
- if ( ! file_exists(APPPATH.'controllers/'.$class.'.php'))
- {
- show_404("{$class}/{$method}");
- }
-
- include_once(APPPATH.'controllers/'.$class.'.php');
+ $error_method = 'index';
}
+
+ $error_class = ucfirst($error_class);
+
+ if ( ! class_exists($error_class, FALSE))
+ {
+ if (file_exists(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php'))
+ {
+ require_once(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php');
+ $e404 = ! class_exists($error_class, FALSE);
+ }
+ // Were we in a directory? If so, check for a global override
+ elseif ( ! empty($RTR->directory) && file_exists(APPPATH.'controllers/'.$error_class.'.php'))
+ {
+ require_once(APPPATH.'controllers/'.$error_class.'.php');
+ if (($e404 = ! class_exists($error_class, FALSE)) === FALSE)
+ {
+ $RTR->directory = '';
+ }
+ }
+ }
+ else
+ {
+ $e404 = FALSE;
+ }
+ }
+
+ // Did we reset the $e404 flag? If so, set the rsegments, starting from index 1
+ if ( ! $e404)
+ {
+ $class = $error_class;
+ $method = $error_method;
+
+ $URI->rsegments = array(
+ 1 => $class,
+ 2 => $method
+ );
}
else
{
- show_404("{$class}/{$method}");
+ show_404($RTR->directory.$class.'/'.$method);
}
}
+ if ($method !== '_remap')
+ {
+ $params = array_slice($URI->rsegments, 2);
+ }
+
/*
* ------------------------------------------------------
* Is there a "pre_controller" hook?
* ------------------------------------------------------
*/
- $EXT->_call_hook('pre_controller');
+ $EXT->call_hook('pre_controller');
/*
* ------------------------------------------------------
@@ -313,53 +504,14 @@
* Is there a "post_controller_constructor" hook?
* ------------------------------------------------------
*/
- $EXT->_call_hook('post_controller_constructor');
+ $EXT->call_hook('post_controller_constructor');
/*
* ------------------------------------------------------
* Call the requested method
* ------------------------------------------------------
*/
- // Is there a "remap" function? If so, we call it instead
- if (method_exists($CI, '_remap'))
- {
- $CI->_remap($method, array_slice($URI->rsegments, 2));
- }
- else
- {
- // is_callable() returns TRUE on some versions of PHP 5 for private and protected
- // methods, so we'll use this workaround for consistent behavior
- if ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($CI))))
- {
- // Check and see if we are using a 404 override and use it.
- if ( ! empty($RTR->routes['404_override']))
- {
- $x = explode('/', $RTR->routes['404_override']);
- $class = $x[0];
- $method = (isset($x[1]) ? $x[1] : 'index');
- if ( ! class_exists($class))
- {
- if ( ! file_exists(APPPATH.'controllers/'.$class.'.php'))
- {
- show_404("{$class}/{$method}");
- }
-
- include_once(APPPATH.'controllers/'.$class.'.php');
- unset($CI);
- $CI = new $class();
- }
- }
- else
- {
- show_404("{$class}/{$method}");
- }
- }
-
- // Call the requested method.
- // Any URI segments present (besides the class/function) will be passed to the method for convenience
- call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, 2));
- }
-
+ call_user_func_array(array(&$CI, $method), $params);
// Mark a benchmark end point
$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end');
@@ -369,14 +521,14 @@
* Is there a "post_controller" hook?
* ------------------------------------------------------
*/
- $EXT->_call_hook('post_controller');
+ $EXT->call_hook('post_controller');
/*
* ------------------------------------------------------
* Send the final rendered output to the browser
* ------------------------------------------------------
*/
- if ($EXT->_call_hook('display_override') === FALSE)
+ if ($EXT->call_hook('display_override') === FALSE)
{
$OUT->_display();
}
@@ -386,18 +538,4 @@
* Is there a "post_system" hook?
* ------------------------------------------------------
*/
- $EXT->_call_hook('post_system');
-
-/*
- * ------------------------------------------------------
- * Close the DB connection if one exists
- * ------------------------------------------------------
- */
- if (class_exists('CI_DB') AND isset($CI->db))
- {
- $CI->db->close();
- }
-
-
-/* End of file CodeIgniter.php */
-/* Location: ./system/core/CodeIgniter.php */
\ No newline at end of file
+ $EXT->call_hook('post_system');
diff --git a/src/system/core/Common.php b/src/system/core/Common.php
index 5e56aa09..b87ce4d6 100644
--- a/src/system/core/Common.php
+++ b/src/system/core/Common.php
@@ -1,20 +1,41 @@
-=');
}
return $_is_php[$version];
@@ -55,43 +75,44 @@ if ( ! function_exists('is_php'))
// ------------------------------------------------------------------------
-/**
- * Tests for file writability
- *
- * is_writable() returns TRUE on Windows servers when you really can't write to
- * the file, based on the read-only attribute. is_writable() is also unreliable
- * on Unix servers if safe_mode is on.
- *
- * @access private
- * @return void
- */
if ( ! function_exists('is_really_writable'))
{
+ /**
+ * Tests for file writability
+ *
+ * is_writable() returns TRUE on Windows servers when you really can't write to
+ * the file, based on the read-only attribute. is_writable() is also unreliable
+ * on Unix servers if safe_mode is on.
+ *
+ * @link https://bugs.php.net/bug.php?id=54709
+ * @param string
+ * @return bool
+ */
function is_really_writable($file)
{
// If we're on a Unix server with safe_mode off we call is_writable
- if (DIRECTORY_SEPARATOR == '/' AND @ini_get("safe_mode") == FALSE)
+ if (DIRECTORY_SEPARATOR === '/' && (is_php('5.4') OR ! ini_get('safe_mode')))
{
return is_writable($file);
}
- // For windows servers and safe_mode "on" installations we'll actually
- // write a file then read it. Bah...
+ /* For Windows servers and safe_mode "on" installations we'll actually
+ * write a file then read it. Bah...
+ */
if (is_dir($file))
{
- $file = rtrim($file, '/').'/'.md5(mt_rand(1,100).mt_rand(1,100));
-
- if (($fp = @fopen($file, FOPEN_WRITE_CREATE)) === FALSE)
+ $file = rtrim($file, '/').'/'.md5(mt_rand());
+ if (($fp = @fopen($file, 'ab')) === FALSE)
{
return FALSE;
}
fclose($fp);
- @chmod($file, DIR_WRITE_MODE);
+ @chmod($file, 0777);
@unlink($file);
return TRUE;
}
- elseif ( ! is_file($file) OR ($fp = @fopen($file, FOPEN_WRITE_CREATE)) === FALSE)
+ elseif ( ! is_file($file) OR ($fp = @fopen($file, 'ab')) === FALSE)
{
return FALSE;
}
@@ -103,26 +124,25 @@ if ( ! function_exists('is_really_writable'))
// ------------------------------------------------------------------------
-/**
-* Class registry
-*
-* This function acts as a singleton. If the requested class does not
-* exist it is instantiated and set to a static variable. If it has
-* previously been instantiated the variable is returned.
-*
-* @access public
-* @param string the class name being requested
-* @param string the directory where the class should be found
-* @param string the class name prefix
-* @return object
-*/
if ( ! function_exists('load_class'))
{
- function &load_class($class, $directory = 'libraries', $prefix = 'CI_')
+ /**
+ * Class registry
+ *
+ * This function acts as a singleton. If the requested class does not
+ * exist it is instantiated and set to a static variable. If it has
+ * previously been instantiated the variable is returned.
+ *
+ * @param string the class name being requested
+ * @param string the directory where the class should be found
+ * @param string an optional argument to pass to the class constructor
+ * @return object
+ */
+ function &load_class($class, $directory = 'libraries', $param = NULL)
{
static $_classes = array();
- // Does the class exist? If so, we're done...
+ // Does the class exist? If so, we're done...
if (isset($_classes[$class]))
{
return $_classes[$class];
@@ -136,60 +156,64 @@ if ( ! function_exists('load_class'))
{
if (file_exists($path.$directory.'/'.$class.'.php'))
{
- $name = $prefix.$class;
+ $name = 'CI_'.$class;
- if (class_exists($name) === FALSE)
+ if (class_exists($name, FALSE) === FALSE)
{
- require($path.$directory.'/'.$class.'.php');
+ require_once($path.$directory.'/'.$class.'.php');
}
break;
}
}
- // Is the request a class extension? If so we load it too
+ // Is the request a class extension? If so we load it too
if (file_exists(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php'))
{
$name = config_item('subclass_prefix').$class;
- if (class_exists($name) === FALSE)
+ if (class_exists($name, FALSE) === FALSE)
{
- require(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php');
+ require_once(APPPATH.$directory.'/'.$name.'.php');
}
}
// Did we find the class?
if ($name === FALSE)
{
- // Note: We use exit() rather then show_error() in order to avoid a
- // self-referencing loop with the Excptions class
- exit('Unable to locate the specified class: '.$class.'.php');
+ // Note: We use exit() rather than show_error() in order to avoid a
+ // self-referencing loop with the Exceptions class
+ set_status_header(503);
+ echo 'Unable to locate the specified class: '.$class.'.php';
+ exit(5); // EXIT_UNK_CLASS
}
// Keep track of what we just loaded
is_loaded($class);
- $_classes[$class] = new $name();
+ $_classes[$class] = isset($param)
+ ? new $name($param)
+ : new $name();
return $_classes[$class];
}
}
// --------------------------------------------------------------------
-/**
-* Keeps track of which libraries have been loaded. This function is
-* called by the load_class() function above
-*
-* @access public
-* @return array
-*/
if ( ! function_exists('is_loaded'))
{
+ /**
+ * Keeps track of which libraries have been loaded. This function is
+ * called by the load_class() function above
+ *
+ * @param string
+ * @return array
+ */
function &is_loaded($class = '')
{
static $_is_loaded = array();
- if ($class != '')
+ if ($class !== '')
{
$_is_loaded[strtolower($class)] = $class;
}
@@ -200,326 +224,502 @@ if ( ! function_exists('is_loaded'))
// ------------------------------------------------------------------------
-/**
-* Loads the main config.php file
-*
-* This function lets us grab the config file even if the Config class
-* hasn't been instantiated yet
-*
-* @access private
-* @return array
-*/
if ( ! function_exists('get_config'))
{
- function &get_config($replace = array())
+ /**
+ * Loads the main config.php file
+ *
+ * This function lets us grab the config file even if the Config class
+ * hasn't been instantiated yet
+ *
+ * @param array
+ * @return array
+ */
+ function &get_config(Array $replace = array())
+ {
+ static $config;
+
+ if (empty($config))
+ {
+ $file_path = APPPATH.'config/config.php';
+ $found = FALSE;
+ if (file_exists($file_path))
+ {
+ $found = TRUE;
+ require($file_path);
+ }
+
+ // Is the config file in the environment folder?
+ if (file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/config.php'))
+ {
+ require($file_path);
+ }
+ elseif ( ! $found)
+ {
+ set_status_header(503);
+ echo 'The configuration file does not exist.';
+ exit(3); // EXIT_CONFIG
+ }
+
+ // Does the $config array exist in the file?
+ if ( ! isset($config) OR ! is_array($config))
+ {
+ set_status_header(503);
+ echo 'Your config file does not appear to be formatted correctly.';
+ exit(3); // EXIT_CONFIG
+ }
+ }
+
+ // Are any values being dynamically added or replaced?
+ foreach ($replace as $key => $val)
+ {
+ $config[$key] = $val;
+ }
+
+ return $config;
+ }
+}
+
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('config_item'))
+{
+ /**
+ * Returns the specified config item
+ *
+ * @param string
+ * @return mixed
+ */
+ function config_item($item)
{
static $_config;
- if (isset($_config))
+ if (empty($_config))
{
- return $_config[0];
+ // references cannot be directly assigned to static variables, so we use an array
+ $_config[0] =& get_config();
}
- // Is the config file in the environment folder?
- if ( ! defined('ENVIRONMENT') OR ! file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/config.php'))
- {
- $file_path = APPPATH.'config/config.php';
- }
-
- // Fetch the config file
- if ( ! file_exists($file_path))
- {
- exit('The configuration file does not exist.');
- }
-
- require($file_path);
-
- // Does the $config array exist in the file?
- if ( ! isset($config) OR ! is_array($config))
- {
- exit('Your config file does not appear to be formatted correctly.');
- }
-
- // Are any values being dynamically replaced?
- if (count($replace) > 0)
- {
- foreach ($replace as $key => $val)
- {
- if (isset($config[$key]))
- {
- $config[$key] = $val;
- }
- }
- }
-
- $_config[0] =& $config;
- return $_config[0];
+ return isset($_config[0][$item]) ? $_config[0][$item] : NULL;
}
}
// ------------------------------------------------------------------------
-/**
-* Returns the specified config item
-*
-* @access public
-* @return mixed
-*/
-if ( ! function_exists('config_item'))
+if ( ! function_exists('get_mimes'))
{
- function config_item($item)
+ /**
+ * Returns the MIME types array from config/mimes.php
+ *
+ * @return array
+ */
+ function &get_mimes()
{
- static $_config_item = array();
+ static $_mimes;
- if ( ! isset($_config_item[$item]))
+ if (empty($_mimes))
{
- $config =& get_config();
-
- if ( ! isset($config[$item]))
+ if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'))
{
- return FALSE;
+ $_mimes = include(APPPATH.'config/'.ENVIRONMENT.'/mimes.php');
+ }
+ elseif (file_exists(APPPATH.'config/mimes.php'))
+ {
+ $_mimes = include(APPPATH.'config/mimes.php');
+ }
+ else
+ {
+ $_mimes = array();
}
- $_config_item[$item] = $config[$item];
}
- return $_config_item[$item];
+ return $_mimes;
+ }
+}
+
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('is_https'))
+{
+ /**
+ * Is HTTPS?
+ *
+ * Determines if the application is accessed via an encrypted
+ * (HTTPS) connection.
+ *
+ * @return bool
+ */
+ function is_https()
+ {
+ if ( ! empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off')
+ {
+ return TRUE;
+ }
+ elseif (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https')
+ {
+ return TRUE;
+ }
+ elseif ( ! empty($_SERVER['HTTP_FRONT_END_HTTPS']) && strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) !== 'off')
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+}
+
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('is_cli'))
+{
+
+ /**
+ * Is CLI?
+ *
+ * Test to see if a request was made from the command line.
+ *
+ * @return bool
+ */
+ function is_cli()
+ {
+ return (PHP_SAPI === 'cli' OR defined('STDIN'));
}
}
// ------------------------------------------------------------------------
-/**
-* Error Handler
-*
-* This function lets us invoke the exception class and
-* display errors using the standard error template located
-* in application/errors/errors.php
-* This function will send the error page directly to the
-* browser and exit.
-*
-* @access public
-* @return void
-*/
if ( ! function_exists('show_error'))
{
+ /**
+ * Error Handler
+ *
+ * This function lets us invoke the exception class and
+ * display errors using the standard error template located
+ * in application/views/errors/error_general.php
+ * This function will send the error page directly to the
+ * browser and exit.
+ *
+ * @param string
+ * @param int
+ * @param string
+ * @return void
+ */
function show_error($message, $status_code = 500, $heading = 'An Error Was Encountered')
{
+ $status_code = abs($status_code);
+ if ($status_code < 100)
+ {
+ $exit_status = $status_code + 9; // 9 is EXIT__AUTO_MIN
+ if ($exit_status > 125) // 125 is EXIT__AUTO_MAX
+ {
+ $exit_status = 1; // EXIT_ERROR
+ }
+
+ $status_code = 500;
+ }
+ else
+ {
+ $exit_status = 1; // EXIT_ERROR
+ }
+
$_error =& load_class('Exceptions', 'core');
echo $_error->show_error($heading, $message, 'error_general', $status_code);
- exit;
+ exit($exit_status);
}
}
// ------------------------------------------------------------------------
-/**
-* 404 Page Handler
-*
-* This function is similar to the show_error() function above
-* However, instead of the standard error template it displays
-* 404 errors.
-*
-* @access public
-* @return void
-*/
if ( ! function_exists('show_404'))
{
+ /**
+ * 404 Page Handler
+ *
+ * This function is similar to the show_error() function above
+ * However, instead of the standard error template it displays
+ * 404 errors.
+ *
+ * @param string
+ * @param bool
+ * @return void
+ */
function show_404($page = '', $log_error = TRUE)
{
$_error =& load_class('Exceptions', 'core');
$_error->show_404($page, $log_error);
- exit;
+ exit(4); // EXIT_UNKNOWN_FILE
}
}
// ------------------------------------------------------------------------
-/**
-* Error Logging Interface
-*
-* We use this as a simple mechanism to access the logging
-* class and send messages to be logged.
-*
-* @access public
-* @return void
-*/
if ( ! function_exists('log_message'))
{
- function log_message($level = 'error', $message, $php_error = FALSE)
+ /**
+ * Error Logging Interface
+ *
+ * We use this as a simple mechanism to access the logging
+ * class and send messages to be logged.
+ *
+ * @param string the error level: 'error', 'debug' or 'info'
+ * @param string the error message
+ * @return void
+ */
+ function log_message($level, $message)
{
static $_log;
- if (config_item('log_threshold') == 0)
+ if ($_log === NULL)
+ {
+ // references cannot be directly assigned to static variables, so we use an array
+ $_log[0] =& load_class('Log', 'core');
+ }
+
+ $_log[0]->write_log($level, $message);
+ }
+}
+
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('set_status_header'))
+{
+ /**
+ * Set HTTP Status Header
+ *
+ * @param int the status code
+ * @param string
+ * @return void
+ */
+ function set_status_header($code = 200, $text = '')
+ {
+ if (is_cli())
{
return;
}
- $_log =& load_class('Log');
- $_log->write_log($level, $message, $php_error);
- }
-}
-
-// ------------------------------------------------------------------------
-
-/**
- * Set HTTP Status Header
- *
- * @access public
- * @param int the status code
- * @param string
- * @return void
- */
-if ( ! function_exists('set_status_header'))
-{
- function set_status_header($code = 200, $text = '')
- {
- $stati = array(
- 200 => 'OK',
- 201 => 'Created',
- 202 => 'Accepted',
- 203 => 'Non-Authoritative Information',
- 204 => 'No Content',
- 205 => 'Reset Content',
- 206 => 'Partial Content',
-
- 300 => 'Multiple Choices',
- 301 => 'Moved Permanently',
- 302 => 'Found',
- 304 => 'Not Modified',
- 305 => 'Use Proxy',
- 307 => 'Temporary Redirect',
-
- 400 => 'Bad Request',
- 401 => 'Unauthorized',
- 403 => 'Forbidden',
- 404 => 'Not Found',
- 405 => 'Method Not Allowed',
- 406 => 'Not Acceptable',
- 407 => 'Proxy Authentication Required',
- 408 => 'Request Timeout',
- 409 => 'Conflict',
- 410 => 'Gone',
- 411 => 'Length Required',
- 412 => 'Precondition Failed',
- 413 => 'Request Entity Too Large',
- 414 => 'Request-URI Too Long',
- 415 => 'Unsupported Media Type',
- 416 => 'Requested Range Not Satisfiable',
- 417 => 'Expectation Failed',
-
- 500 => 'Internal Server Error',
- 501 => 'Not Implemented',
- 502 => 'Bad Gateway',
- 503 => 'Service Unavailable',
- 504 => 'Gateway Timeout',
- 505 => 'HTTP Version Not Supported'
- );
-
- if ($code == '' OR ! is_numeric($code))
+ if (empty($code) OR ! is_numeric($code))
{
show_error('Status codes must be numeric', 500);
}
- if (isset($stati[$code]) AND $text == '')
+ if (empty($text))
{
- $text = $stati[$code];
+ is_int($code) OR $code = (int) $code;
+ $stati = array(
+ 100 => 'Continue',
+ 101 => 'Switching Protocols',
+
+ 200 => 'OK',
+ 201 => 'Created',
+ 202 => 'Accepted',
+ 203 => 'Non-Authoritative Information',
+ 204 => 'No Content',
+ 205 => 'Reset Content',
+ 206 => 'Partial Content',
+
+ 300 => 'Multiple Choices',
+ 301 => 'Moved Permanently',
+ 302 => 'Found',
+ 303 => 'See Other',
+ 304 => 'Not Modified',
+ 305 => 'Use Proxy',
+ 307 => 'Temporary Redirect',
+
+ 400 => 'Bad Request',
+ 401 => 'Unauthorized',
+ 402 => 'Payment Required',
+ 403 => 'Forbidden',
+ 404 => 'Not Found',
+ 405 => 'Method Not Allowed',
+ 406 => 'Not Acceptable',
+ 407 => 'Proxy Authentication Required',
+ 408 => 'Request Timeout',
+ 409 => 'Conflict',
+ 410 => 'Gone',
+ 411 => 'Length Required',
+ 412 => 'Precondition Failed',
+ 413 => 'Request Entity Too Large',
+ 414 => 'Request-URI Too Long',
+ 415 => 'Unsupported Media Type',
+ 416 => 'Requested Range Not Satisfiable',
+ 417 => 'Expectation Failed',
+ 422 => 'Unprocessable Entity',
+
+ 500 => 'Internal Server Error',
+ 501 => 'Not Implemented',
+ 502 => 'Bad Gateway',
+ 503 => 'Service Unavailable',
+ 504 => 'Gateway Timeout',
+ 505 => 'HTTP Version Not Supported'
+ );
+
+ if (isset($stati[$code]))
+ {
+ $text = $stati[$code];
+ }
+ else
+ {
+ show_error('No status text available. Please check your status code number or supply your own message text.', 500);
+ }
}
- if ($text == '')
+ if (strpos(PHP_SAPI, 'cgi') === 0)
{
- show_error('No status text available. Please check your status code number or supply your own message text.', 500);
- }
-
- $server_protocol = (isset($_SERVER['SERVER_PROTOCOL'])) ? $_SERVER['SERVER_PROTOCOL'] : FALSE;
-
- if (substr(php_sapi_name(), 0, 3) == 'cgi')
- {
- header("Status: {$code} {$text}", TRUE);
- }
- elseif ($server_protocol == 'HTTP/1.1' OR $server_protocol == 'HTTP/1.0')
- {
- header($server_protocol." {$code} {$text}", TRUE, $code);
+ header('Status: '.$code.' '.$text, TRUE);
}
else
{
- header("HTTP/1.1 {$code} {$text}", TRUE, $code);
+ $server_protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1';
+ header($server_protocol.' '.$code.' '.$text, TRUE, $code);
}
}
}
// --------------------------------------------------------------------
-/**
-* Exception Handler
-*
-* This is the custom exception handler that is declaired at the top
-* of Codeigniter.php. The main reason we use this is to permit
-* PHP errors to be logged in our own log files since the user may
-* not have access to server logs. Since this function
-* effectively intercepts PHP errors, however, we also need
-* to display errors based on the current error_reporting level.
-* We do that with the use of a PHP error template.
-*
-* @access private
-* @return void
-*/
-if ( ! function_exists('_exception_handler'))
+if ( ! function_exists('_error_handler'))
{
- function _exception_handler($severity, $message, $filepath, $line)
+ /**
+ * Error Handler
+ *
+ * This is the custom error handler that is declared at the (relative)
+ * top of CodeIgniter.php. The main reason we use this is to permit
+ * PHP errors to be logged in our own log files since the user may
+ * not have access to server logs. Since this function effectively
+ * intercepts PHP errors, however, we also need to display errors
+ * based on the current error_reporting level.
+ * We do that with the use of a PHP error template.
+ *
+ * @param int $severity
+ * @param string $message
+ * @param string $filepath
+ * @param int $line
+ * @return void
+ */
+ function _error_handler($severity, $message, $filepath, $line)
{
- // We don't bother with "strict" notices since they tend to fill up
- // the log file with excess information that isn't normally very helpful.
- if ($severity == E_STRICT)
+ $is_error = (((E_ERROR | E_COMPILE_ERROR | E_CORE_ERROR | E_USER_ERROR) & $severity) === $severity);
+
+ // When an error occurred, set the status header to '500 Internal Server Error'
+ // to indicate to the client something went wrong.
+ // This can't be done within the $_error->show_php_error method because
+ // it is only called when the display_errors flag is set (which isn't usually
+ // the case in a production environment) or when errors are ignored because
+ // they are above the error_reporting threshold.
+ if ($is_error)
+ {
+ set_status_header(500);
+ }
+
+ // Should we ignore the error? We'll get the current error_reporting
+ // level and add its bits with the severity bits to find out.
+ if (($severity & error_reporting()) !== $severity)
{
return;
}
$_error =& load_class('Exceptions', 'core');
+ $_error->log_exception($severity, $message, $filepath, $line);
- // Should we display the error? We'll get the current error_reporting
- // level and add its bits with the severity bits to find out.
- if (($severity & error_reporting()) == $severity)
+ // Should we display the error?
+ if (str_ireplace(array('off', 'none', 'no', 'false', 'null'), '', ini_get('display_errors')))
{
$_error->show_php_error($severity, $message, $filepath, $line);
}
- // Should we log the error? No? We're done...
- if (config_item('log_threshold') == 0)
+ // If the error is fatal, the execution of the script should be stopped because
+ // errors can't be recovered from. Halting the script conforms with PHP's
+ // default error handling. See http://www.php.net/manual/en/errorfunc.constants.php
+ if ($is_error)
{
- return;
+ exit(1); // EXIT_ERROR
+ }
+ }
+}
+
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('_exception_handler'))
+{
+ /**
+ * Exception Handler
+ *
+ * Sends uncaught exceptions to the logger and displays them
+ * only if display_errors is On so that they don't show up in
+ * production environments.
+ *
+ * @param Exception $exception
+ * @return void
+ */
+ function _exception_handler($exception)
+ {
+ $_error =& load_class('Exceptions', 'core');
+ $_error->log_exception('error', 'Exception: '.$exception->getMessage(), $exception->getFile(), $exception->getLine());
+
+ // Should we display the error?
+ if (str_ireplace(array('off', 'none', 'no', 'false', 'null'), '', ini_get('display_errors')))
+ {
+ $_error->show_exception($exception);
}
- $_error->log_exception($severity, $message, $filepath, $line);
+ exit(1); // EXIT_ERROR
+ }
+}
+
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('_shutdown_handler'))
+{
+ /**
+ * Shutdown Handler
+ *
+ * This is the shutdown handler that is declared at the top
+ * of CodeIgniter.php. The main reason we use this is to simulate
+ * a complete custom exception handler.
+ *
+ * E_STRICT is purposively neglected because such events may have
+ * been caught. Duplication or none? None is preferred for now.
+ *
+ * @link http://insomanic.me.uk/post/229851073/php-trick-catching-fatal-errors-e-error-with-a
+ * @return void
+ */
+ function _shutdown_handler()
+ {
+ $last_error = error_get_last();
+ if (isset($last_error) &&
+ ($last_error['type'] & (E_ERROR | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING)))
+ {
+ _error_handler($last_error['type'], $last_error['message'], $last_error['file'], $last_error['line']);
+ }
}
}
// --------------------------------------------------------------------
-/**
- * Remove Invisible Characters
- *
- * This prevents sandwiching null characters
- * between ascii characters, like Java\0script.
- *
- * @access public
- * @param string
- * @return string
- */
if ( ! function_exists('remove_invisible_characters'))
{
+ /**
+ * Remove Invisible Characters
+ *
+ * This prevents sandwiching null characters
+ * between ascii characters, like Java\0script.
+ *
+ * @param string
+ * @param bool
+ * @return string
+ */
function remove_invisible_characters($str, $url_encoded = TRUE)
{
$non_displayables = array();
-
- // every control character except newline (dec 10)
- // carriage return (dec 13), and horizontal tab (dec 09)
-
+
+ // every control character except newline (dec 10),
+ // carriage return (dec 13) and horizontal tab (dec 09)
if ($url_encoded)
{
$non_displayables[] = '/%0[0-8bcef]/'; // url encoded 00-08, 11, 12, 14, 15
$non_displayables[] = '/%1[0-9a-f]/'; // url encoded 16-31
}
-
+
$non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S'; // 00-08, 11, 12, 14-31, 127
do
@@ -534,27 +734,118 @@ if ( ! function_exists('remove_invisible_characters'))
// ------------------------------------------------------------------------
-/**
-* Returns HTML escaped variable
-*
-* @access public
-* @param mixed
-* @return mixed
-*/
if ( ! function_exists('html_escape'))
{
- function html_escape($var)
+ /**
+ * Returns HTML escaped variable.
+ *
+ * @param mixed $var The input string or array of strings to be escaped.
+ * @param bool $double_encode $double_encode set to FALSE prevents escaping twice.
+ * @return mixed The escaped string or array of strings as a result.
+ */
+ function html_escape($var, $double_encode = TRUE)
{
+ if (empty($var))
+ {
+ return $var;
+ }
+
if (is_array($var))
{
- return array_map('html_escape', $var);
- }
- else
- {
- return htmlspecialchars($var, ENT_QUOTES, config_item('charset'));
+ foreach (array_keys($var) as $key)
+ {
+ $var[$key] = html_escape($var[$key], $double_encode);
+ }
+
+ return $var;
}
+
+ return htmlspecialchars($var, ENT_QUOTES, config_item('charset'), $double_encode);
}
}
-/* End of file Common.php */
-/* Location: ./system/core/Common.php */
\ No newline at end of file
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('_stringify_attributes'))
+{
+ /**
+ * Stringify attributes for use in HTML tags.
+ *
+ * Helper function used to convert a string, array, or object
+ * of attributes to a string.
+ *
+ * @param mixed string, array, object
+ * @param bool
+ * @return string
+ */
+ function _stringify_attributes($attributes, $js = FALSE)
+ {
+ $atts = NULL;
+
+ if (empty($attributes))
+ {
+ return $atts;
+ }
+
+ if (is_string($attributes))
+ {
+ return ' '.$attributes;
+ }
+
+ $attributes = (array) $attributes;
+
+ foreach ($attributes as $key => $val)
+ {
+ $atts .= ($js) ? $key.'='.$val.',' : ' '.$key.'="'.$val.'"';
+ }
+
+ return rtrim($atts, ',');
+ }
+}
+
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('function_usable'))
+{
+ /**
+ * Function usable
+ *
+ * Executes a function_exists() check, and if the Suhosin PHP
+ * extension is loaded - checks whether the function that is
+ * checked might be disabled in there as well.
+ *
+ * This is useful as function_exists() will return FALSE for
+ * functions disabled via the *disable_functions* php.ini
+ * setting, but not for *suhosin.executor.func.blacklist* and
+ * *suhosin.executor.disable_eval*. These settings will just
+ * terminate script execution if a disabled function is executed.
+ *
+ * The above described behavior turned out to be a bug in Suhosin,
+ * but even though a fix was commited for 0.9.34 on 2012-02-12,
+ * that version is yet to be released. This function will therefore
+ * be just temporary, but would probably be kept for a few years.
+ *
+ * @link http://www.hardened-php.net/suhosin/
+ * @param string $function_name Function to check for
+ * @return bool TRUE if the function exists and is safe to call,
+ * FALSE otherwise.
+ */
+ function function_usable($function_name)
+ {
+ static $_suhosin_func_blacklist;
+
+ if (function_exists($function_name))
+ {
+ if ( ! isset($_suhosin_func_blacklist))
+ {
+ $_suhosin_func_blacklist = extension_loaded('suhosin')
+ ? explode(',', trim(ini_get('suhosin.executor.func.blacklist')))
+ : array();
+ }
+
+ return ! in_array($function_name, $_suhosin_func_blacklist, TRUE);
+ }
+
+ return FALSE;
+ }
+}
diff --git a/src/system/core/Config.php b/src/system/core/Config.php
index 45641faa..ca6fb379 100644
--- a/src/system/core/Config.php
+++ b/src/system/core/Config.php
@@ -1,23 +1,44 @@
-config =& get_config();
- log_message('debug', "Config Class Initialized");
// Set the base_url automatically if none was provided
- if ($this->config['base_url'] == '')
+ if (empty($this->config['base_url']))
{
- // The regular expression is only a basic validation for a valid "Host" header.
- // It's not exhaustive, only checks for valid characters.
- if (isset($_SERVER['HTTP_HOST']) && preg_match('/^((\[[0-9a-f:]+\])|(\d{1,3}(\.\d{1,3}){3})|[a-z0-9\-\.]+)(:\d+)?$/i', $_SERVER['HTTP_HOST']))
+ if (isset($_SERVER['SERVER_ADDR']))
{
- $base_url = (empty($_SERVER['HTTPS']) OR strtolower($_SERVER['HTTPS']) === 'off') ? 'http' : 'https';
- $base_url .= '://'. $_SERVER['HTTP_HOST'];
- $base_url .= substr($_SERVER['SCRIPT_NAME'], 0, strpos($_SERVER['SCRIPT_NAME'], basename($_SERVER['SCRIPT_FILENAME'])));
- }
+ if (strpos($_SERVER['SERVER_ADDR'], ':') !== FALSE)
+ {
+ $server_addr = '['.$_SERVER['SERVER_ADDR'].']';
+ }
+ else
+ {
+ $server_addr = $_SERVER['SERVER_ADDR'];
+ }
+ $base_url = (is_https() ? 'https' : 'http').'://'.$server_addr
+ .substr($_SERVER['SCRIPT_NAME'], 0, strpos($_SERVER['SCRIPT_NAME'], basename($_SERVER['SCRIPT_FILENAME'])));
+ }
else
{
$base_url = 'http://localhost/';
@@ -83,6 +109,8 @@ class CI_Config {
$this->set_item('base_url', $base_url);
}
+
+ log_message('info', 'Config Class Initialized');
}
// --------------------------------------------------------------------
@@ -90,91 +118,71 @@ class CI_Config {
/**
* Load Config File
*
- * @access public
- * @param string the config file name
- * @param boolean if configuration values should be loaded into their own section
- * @param boolean true if errors should just return false, false if an error message should be displayed
- * @return boolean if the file was loaded correctly
+ * @param string $file Configuration file name
+ * @param bool $use_sections Whether configuration values should be loaded into their own section
+ * @param bool $fail_gracefully Whether to just return FALSE or display an error message
+ * @return bool TRUE if the file was loaded correctly or FALSE on failure
*/
- function load($file = '', $use_sections = FALSE, $fail_gracefully = FALSE)
+ public function load($file = '', $use_sections = FALSE, $fail_gracefully = FALSE)
{
- $file = ($file == '') ? 'config' : str_replace('.php', '', $file);
- $found = FALSE;
+ $file = ($file === '') ? 'config' : str_replace('.php', '', $file);
$loaded = FALSE;
- $check_locations = defined('ENVIRONMENT')
- ? array(ENVIRONMENT.'/'.$file, $file)
- : array($file);
-
foreach ($this->_config_paths as $path)
{
- foreach ($check_locations as $location)
+ foreach (array($file, ENVIRONMENT.DIRECTORY_SEPARATOR.$file) as $location)
{
$file_path = $path.'config/'.$location.'.php';
-
if (in_array($file_path, $this->is_loaded, TRUE))
{
- $loaded = TRUE;
- continue 2;
+ return TRUE;
}
- if (file_exists($file_path))
+ if ( ! file_exists($file_path))
{
- $found = TRUE;
- break;
+ continue;
}
- }
- if ($found === FALSE)
- {
- continue;
- }
+ include($file_path);
- include($file_path);
-
- if ( ! isset($config) OR ! is_array($config))
- {
- if ($fail_gracefully === TRUE)
+ if ( ! isset($config) OR ! is_array($config))
{
- return FALSE;
+ if ($fail_gracefully === TRUE)
+ {
+ return FALSE;
+ }
+
+ show_error('Your '.$file_path.' file does not appear to contain a valid configuration array.');
}
- show_error('Your '.$file_path.' file does not appear to contain a valid configuration array.');
- }
- if ($use_sections === TRUE)
- {
- if (isset($this->config[$file]))
+ if ($use_sections === TRUE)
{
- $this->config[$file] = array_merge($this->config[$file], $config);
+ $this->config[$file] = isset($this->config[$file])
+ ? array_merge($this->config[$file], $config)
+ : $config;
}
else
{
- $this->config[$file] = $config;
+ $this->config = array_merge($this->config, $config);
}
- }
- else
- {
- $this->config = array_merge($this->config, $config);
- }
- $this->is_loaded[] = $file_path;
- unset($config);
-
- $loaded = TRUE;
- log_message('debug', 'Config file loaded: '.$file_path);
- break;
+ $this->is_loaded[] = $file_path;
+ $config = NULL;
+ $loaded = TRUE;
+ log_message('debug', 'Config file loaded: '.$file_path);
+ }
}
- if ($loaded === FALSE)
+ if ($loaded === TRUE)
{
- if ($fail_gracefully === TRUE)
- {
- return FALSE;
- }
- show_error('The configuration file '.$file.'.php does not exist.');
+ return TRUE;
+ }
+ elseif ($fail_gracefully === TRUE)
+ {
+ return FALSE;
}
- return TRUE;
+ show_error('The configuration file '.$file.'.php does not exist.');
}
// --------------------------------------------------------------------
@@ -182,59 +190,35 @@ class CI_Config {
/**
* Fetch a config file item
*
- *
- * @access public
- * @param string the config item name
- * @param string the index name
- * @param bool
- * @return string
+ * @param string $item Config item name
+ * @param string $index Index name
+ * @return string|null The configuration item or NULL if the item doesn't exist
*/
- function item($item, $index = '')
+ public function item($item, $index = '')
{
if ($index == '')
{
- if ( ! isset($this->config[$item]))
- {
- return FALSE;
- }
-
- $pref = $this->config[$item];
- }
- else
- {
- if ( ! isset($this->config[$index]))
- {
- return FALSE;
- }
-
- if ( ! isset($this->config[$index][$item]))
- {
- return FALSE;
- }
-
- $pref = $this->config[$index][$item];
+ return isset($this->config[$item]) ? $this->config[$item] : NULL;
}
- return $pref;
+ return isset($this->config[$index], $this->config[$index][$item]) ? $this->config[$index][$item] : NULL;
}
// --------------------------------------------------------------------
/**
- * Fetch a config file item - adds slash after item (if item is not empty)
+ * Fetch a config file item with slash appended (if not empty)
*
- * @access public
- * @param string the config item name
- * @param bool
- * @return string
+ * @param string $item Config item name
+ * @return string|null The configuration item or NULL if the item doesn't exist
*/
- function slash_item($item)
+ public function slash_item($item)
{
if ( ! isset($this->config[$item]))
{
- return FALSE;
+ return NULL;
}
- if( trim($this->config[$item]) == '')
+ elseif (trim($this->config[$item]) === '')
{
return '';
}
@@ -246,80 +230,125 @@ class CI_Config {
/**
* Site URL
+ *
* Returns base_url . index_page [. uri_string]
*
- * @access public
- * @param string the URI string
+ * @uses CI_Config::_uri_string()
+ *
+ * @param string|string[] $uri URI string or an array of segments
+ * @param string $protocol
* @return string
*/
- function site_url($uri = '')
+ public function site_url($uri = '', $protocol = NULL)
{
- if ($uri == '')
+ $base_url = $this->slash_item('base_url');
+
+ if (isset($protocol))
{
- return $this->slash_item('base_url').$this->item('index_page');
+ // For protocol-relative links
+ if ($protocol === '')
+ {
+ $base_url = substr($base_url, strpos($base_url, '//'));
+ }
+ else
+ {
+ $base_url = $protocol.substr($base_url, strpos($base_url, '://'));
+ }
}
- if ($this->item('enable_query_strings') == FALSE)
+ if (empty($uri))
{
- $suffix = ($this->item('url_suffix') == FALSE) ? '' : $this->item('url_suffix');
- return $this->slash_item('base_url').$this->slash_item('index_page').$this->_uri_string($uri).$suffix;
+ return $base_url.$this->item('index_page');
}
- else
+
+ $uri = $this->_uri_string($uri);
+
+ if ($this->item('enable_query_strings') === FALSE)
{
- return $this->slash_item('base_url').$this->item('index_page').'?'.$this->_uri_string($uri);
+ $suffix = isset($this->config['url_suffix']) ? $this->config['url_suffix'] : '';
+
+ if ($suffix !== '')
+ {
+ if (($offset = strpos($uri, '?')) !== FALSE)
+ {
+ $uri = substr($uri, 0, $offset).$suffix.substr($uri, $offset);
+ }
+ else
+ {
+ $uri .= $suffix;
+ }
+ }
+
+ return $base_url.$this->slash_item('index_page').$uri;
}
+ elseif (strpos($uri, '?') === FALSE)
+ {
+ $uri = '?'.$uri;
+ }
+
+ return $base_url.$this->item('index_page').$uri;
}
// -------------------------------------------------------------
/**
* Base URL
+ *
* Returns base_url [. uri_string]
*
- * @access public
- * @param string $uri
- * @return string
+ * @uses CI_Config::_uri_string()
+ *
+ * @param string|string[] $uri URI string or an array of segments
+ * @param string $protocol
+ * @return string
*/
- function base_url($uri = '')
+ public function base_url($uri = '', $protocol = NULL)
{
- return $this->slash_item('base_url').ltrim($this->_uri_string($uri), '/');
+ $base_url = $this->slash_item('base_url');
+
+ if (isset($protocol))
+ {
+ // For protocol-relative links
+ if ($protocol === '')
+ {
+ $base_url = substr($base_url, strpos($base_url, '//'));
+ }
+ else
+ {
+ $base_url = $protocol.substr($base_url, strpos($base_url, '://'));
+ }
+ }
+
+ return $base_url.ltrim($this->_uri_string($uri), '/');
}
// -------------------------------------------------------------
/**
- * Build URI string for use in Config::site_url() and Config::base_url()
+ * Build URI string
*
- * @access protected
- * @param $uri
- * @return string
+ * @used-by CI_Config::site_url()
+ * @used-by CI_Config::base_url()
+ *
+ * @param string|string[] $uri URI string or an array of segments
+ * @return string
*/
protected function _uri_string($uri)
{
- if ($this->item('enable_query_strings') == FALSE)
+ if ($this->item('enable_query_strings') === FALSE)
{
if (is_array($uri))
{
$uri = implode('/', $uri);
}
- $uri = trim($uri, '/');
+ return trim($uri, '/');
}
- else
+ elseif (is_array($uri))
{
- if (is_array($uri))
- {
- $i = 0;
- $str = '';
- foreach ($uri as $key => $val)
- {
- $prefix = ($i == 0) ? '' : '&';
- $str .= $prefix.$key.'='.$val;
- $i++;
- }
- $uri = $str;
- }
+ return http_build_query($uri);
}
- return $uri;
+
+ return $uri;
}
// --------------------------------------------------------------------
@@ -327,12 +356,12 @@ class CI_Config {
/**
* System URL
*
- * @access public
+ * @deprecated 3.0.0 Encourages insecure practices
* @return string
*/
- function system_url()
+ public function system_url()
{
- $x = explode("/", preg_replace("|/*(.+?)/*$|", "\\1", BASEPATH));
+ $x = explode('/', preg_replace('|/*(.+?)/*$|', '\\1', BASEPATH));
return $this->slash_item('base_url').end($x).'/';
}
@@ -341,42 +370,13 @@ class CI_Config {
/**
* Set a config file item
*
- * @access public
- * @param string the config item key
- * @param string the config item value
+ * @param string $item Config item key
+ * @param string $value Config item value
* @return void
*/
- function set_item($item, $value)
+ public function set_item($item, $value)
{
$this->config[$item] = $value;
}
- // --------------------------------------------------------------------
-
- /**
- * Assign to Config
- *
- * This function is called by the front controller (CodeIgniter.php)
- * after the Config class is instantiated. It permits config items
- * to be assigned or overriden by variables contained in the index.php file
- *
- * @access private
- * @param array
- * @return void
- */
- function _assign_to_config($items = array())
- {
- if (is_array($items))
- {
- foreach ($items as $key => $val)
- {
- $this->set_item($key, $val);
- }
- }
- }
}
-
-// END CI_Config class
-
-/* End of file Config.php */
-/* Location: ./system/core/Config.php */
diff --git a/src/system/core/Controller.php b/src/system/core/Controller.php
index e05362ca..83b3df26 100644
--- a/src/system/core/Controller.php
+++ b/src/system/core/Controller.php
@@ -1,23 +1,44 @@
-load =& load_class('Loader', 'core');
-
$this->load->initialize();
-
- log_message('debug', "Controller Class Initialized");
+ log_message('info', 'Controller Class Initialized');
}
+ // --------------------------------------------------------------------
+
+ /**
+ * Get the CI singleton
+ *
+ * @static
+ * @return object
+ */
public static function &get_instance()
{
return self::$instance;
}
-}
-// END Controller class
-/* End of file Controller.php */
-/* Location: ./system/core/Controller.php */
\ No newline at end of file
+}
diff --git a/src/system/core/Exceptions.php b/src/system/core/Exceptions.php
index 4ca2fafb..a1c6a197 100644
--- a/src/system/core/Exceptions.php
+++ b/src/system/core/Exceptions.php
@@ -1,20 +1,41 @@
- 'Error',
- E_WARNING => 'Warning',
- E_PARSE => 'Parsing Error',
- E_NOTICE => 'Notice',
- E_CORE_ERROR => 'Core Error',
- E_CORE_WARNING => 'Core Warning',
- E_COMPILE_ERROR => 'Compile Error',
- E_COMPILE_WARNING => 'Compile Warning',
- E_USER_ERROR => 'User Error',
- E_USER_WARNING => 'User Warning',
- E_USER_NOTICE => 'User Notice',
- E_STRICT => 'Runtime Notice'
- );
-
+ public $levels = array(
+ E_ERROR => 'Error',
+ E_WARNING => 'Warning',
+ E_PARSE => 'Parsing Error',
+ E_NOTICE => 'Notice',
+ E_CORE_ERROR => 'Core Error',
+ E_CORE_WARNING => 'Core Warning',
+ E_COMPILE_ERROR => 'Compile Error',
+ E_COMPILE_WARNING => 'Compile Warning',
+ E_USER_ERROR => 'User Error',
+ E_USER_WARNING => 'User Warning',
+ E_USER_NOTICE => 'User Notice',
+ E_STRICT => 'Runtime Notice'
+ );
/**
- * Constructor
+ * Class constructor
+ *
+ * @return void
*/
public function __construct()
{
$this->ob_level = ob_get_level();
- // Note: Do not log messages from this constructor.
+ // Note: Do not log messages from this constructor.
}
// --------------------------------------------------------------------
@@ -76,45 +91,52 @@ class CI_Exceptions {
/**
* Exception Logger
*
- * This function logs PHP generated error messages
+ * Logs PHP generated error messages
*
- * @access private
- * @param string the error severity
- * @param string the error string
- * @param string the error filepath
- * @param string the error line number
- * @return string
+ * @param int $severity Log level
+ * @param string $message Error message
+ * @param string $filepath File path
+ * @param int $line Line number
+ * @return void
*/
- function log_exception($severity, $message, $filepath, $line)
+ public function log_exception($severity, $message, $filepath, $line)
{
- $severity = ( ! isset($this->levels[$severity])) ? $severity : $this->levels[$severity];
-
- log_message('error', 'Severity: '.$severity.' --> '.$message. ' '.$filepath.' '.$line, TRUE);
+ $severity = isset($this->levels[$severity]) ? $this->levels[$severity] : $severity;
+ log_message('error', 'Severity: '.$severity.' --> '.$message.' '.$filepath.' '.$line);
}
// --------------------------------------------------------------------
/**
- * 404 Page Not Found Handler
+ * 404 Error Handler
*
- * @access private
- * @param string the page
- * @param bool log error yes/no
- * @return string
+ * @uses CI_Exceptions::show_error()
+ *
+ * @param string $page Page URI
+ * @param bool $log_error Whether to log the error
+ * @return void
*/
- function show_404($page = '', $log_error = TRUE)
+ public function show_404($page = '', $log_error = TRUE)
{
- $heading = "404 Page Not Found";
- $message = "The page you requested was not found.";
+ if (is_cli())
+ {
+ $heading = 'Not Found';
+ $message = 'The controller/method pair you requested was not found.';
+ }
+ else
+ {
+ $heading = '404 Page Not Found';
+ $message = 'The page you requested was not found.';
+ }
// By default we log this, but allow a dev to skip it
if ($log_error)
{
- log_message('error', '404 Page Not Found --> '.$page);
+ log_message('error', $heading.': '.$page);
}
echo $this->show_error($heading, $message, 'error_404', 404);
- exit;
+ exit(4); // EXIT_UNKNOWN_FILE
}
// --------------------------------------------------------------------
@@ -122,29 +144,42 @@ class CI_Exceptions {
/**
* General Error Page
*
- * This function takes an error message as input
- * (either as a string or an array) and displays
- * it using the specified template.
+ * Takes an error message as input (either as a string or an array)
+ * and displays it using the specified template.
*
- * @access private
- * @param string the heading
- * @param string the message
- * @param string the template name
- * @param int the status code
- * @return string
+ * @param string $heading Page heading
+ * @param string|string[] $message Error message
+ * @param string $template Template name
+ * @param int $status_code (default: 500)
+ *
+ * @return string Error page output
*/
- function show_error($heading, $message, $template = 'error_general', $status_code = 500)
+ public function show_error($heading, $message, $template = 'error_general', $status_code = 500)
{
- set_status_header($status_code);
+ $templates_path = config_item('error_views_path');
+ if (empty($templates_path))
+ {
+ $templates_path = VIEWPATH.'errors'.DIRECTORY_SEPARATOR;
+ }
- $message = ''.implode('
', ( ! is_array($message)) ? array($message) : $message).'
';
+ if (is_cli())
+ {
+ $message = "\t".(is_array($message) ? implode("\n\t", $message) : $message);
+ $template = 'cli'.DIRECTORY_SEPARATOR.$template;
+ }
+ else
+ {
+ set_status_header($status_code);
+ $message = ''.(is_array($message) ? implode('
', $message) : $message).'
';
+ $template = 'html'.DIRECTORY_SEPARATOR.$template;
+ }
if (ob_get_level() > $this->ob_level + 1)
{
ob_end_flush();
}
ob_start();
- include(APPPATH.'errors/'.$template.'.php');
+ include($templates_path.$template.'.php');
$buffer = ob_get_contents();
ob_end_clean();
return $buffer;
@@ -152,27 +187,78 @@ class CI_Exceptions {
// --------------------------------------------------------------------
+ public function show_exception($exception)
+ {
+ $templates_path = config_item('error_views_path');
+ if (empty($templates_path))
+ {
+ $templates_path = VIEWPATH.'errors'.DIRECTORY_SEPARATOR;
+ }
+
+ $message = $exception->getMessage();
+ if (empty($message))
+ {
+ $message = '(null)';
+ }
+
+ if (is_cli())
+ {
+ $templates_path .= 'cli'.DIRECTORY_SEPARATOR;
+ }
+ else
+ {
+ set_status_header(500);
+ $templates_path .= 'html'.DIRECTORY_SEPARATOR;
+ }
+
+ if (ob_get_level() > $this->ob_level + 1)
+ {
+ ob_end_flush();
+ }
+
+ ob_start();
+ include($templates_path.'error_exception.php');
+ $buffer = ob_get_contents();
+ ob_end_clean();
+ echo $buffer;
+ }
+
+ // --------------------------------------------------------------------
+
/**
* Native PHP error handler
*
- * @access private
- * @param string the error severity
- * @param string the error string
- * @param string the error filepath
- * @param string the error line number
- * @return string
+ * @param int $severity Error level
+ * @param string $message Error message
+ * @param string $filepath File path
+ * @param int $line Line number
+ * @return string Error page output
*/
- function show_php_error($severity, $message, $filepath, $line)
+ public function show_php_error($severity, $message, $filepath, $line)
{
- $severity = ( ! isset($this->levels[$severity])) ? $severity : $this->levels[$severity];
-
- $filepath = str_replace("\\", "/", $filepath);
-
- // For safety reasons we do not show the full file path
- if (FALSE !== strpos($filepath, '/'))
+ $templates_path = config_item('error_views_path');
+ if (empty($templates_path))
{
- $x = explode('/', $filepath);
- $filepath = $x[count($x)-2].'/'.end($x);
+ $templates_path = VIEWPATH.'errors'.DIRECTORY_SEPARATOR;
+ }
+
+ $severity = isset($this->levels[$severity]) ? $this->levels[$severity] : $severity;
+
+ // For safety reasons we don't show the full file path in non-CLI requests
+ if ( ! is_cli())
+ {
+ $filepath = str_replace('\\', '/', $filepath);
+ if (FALSE !== strpos($filepath, '/'))
+ {
+ $x = explode('/', $filepath);
+ $filepath = $x[count($x)-2].'/'.end($x);
+ }
+
+ $template = 'html'.DIRECTORY_SEPARATOR.'error_php';
+ }
+ else
+ {
+ $template = 'cli'.DIRECTORY_SEPARATOR.'error_php';
}
if (ob_get_level() > $this->ob_level + 1)
@@ -180,15 +266,10 @@ class CI_Exceptions {
ob_end_flush();
}
ob_start();
- include(APPPATH.'errors/error_php.php');
+ include($templates_path.$template.'.php');
$buffer = ob_get_contents();
ob_end_clean();
echo $buffer;
}
-
}
-// END Exceptions Class
-
-/* End of file Exceptions.php */
-/* Location: ./system/core/Exceptions.php */
\ No newline at end of file
diff --git a/src/system/core/Hooks.php b/src/system/core/Hooks.php
index 4dbabb87..856795cb 100644
--- a/src/system/core/Hooks.php
+++ b/src/system/core/Hooks.php
@@ -1,23 +1,44 @@
-_initialize();
- log_message('debug', "Hooks Class Initialized");
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Initialize the Hooks Preferences
- *
- * @access private
* @return void
*/
- function _initialize()
+ public function __construct()
{
$CFG =& load_class('Config', 'core');
+ log_message('info', 'Hooks Class Initialized');
// If hooks are not enabled in the config file
// there is nothing else to do
-
- if ($CFG->item('enable_hooks') == FALSE)
+ if ($CFG->item('enable_hooks') === FALSE)
{
return;
}
// Grab the "hooks" definition file.
- // If there are no hooks, we're done.
-
- if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/hooks.php'))
- {
- include(APPPATH.'config/'.ENVIRONMENT.'/hooks.php');
- }
- elseif (is_file(APPPATH.'config/hooks.php'))
+ if (file_exists(APPPATH.'config/hooks.php'))
{
include(APPPATH.'config/hooks.php');
}
+ if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/hooks.php'))
+ {
+ include(APPPATH.'config/'.ENVIRONMENT.'/hooks.php');
+ }
+ // If there are no hooks, we're done.
if ( ! isset($hook) OR ! is_array($hook))
{
return;
@@ -105,20 +123,21 @@ class CI_Hooks {
/**
* Call Hook
*
- * Calls a particular hook
+ * Calls a particular hook. Called by CodeIgniter.php.
*
- * @access private
- * @param string the hook name
- * @return mixed
+ * @uses CI_Hooks::_run_hook()
+ *
+ * @param string $which Hook name
+ * @return bool TRUE on success or FALSE on failure
*/
- function _call_hook($which = '')
+ public function call_hook($which = '')
{
if ( ! $this->enabled OR ! isset($this->hooks[$which]))
{
return FALSE;
}
- if (isset($this->hooks[$which][0]) AND is_array($this->hooks[$which][0]))
+ if (is_array($this->hooks[$which]) && ! isset($this->hooks[$which]['function']))
{
foreach ($this->hooks[$which] as $val)
{
@@ -140,13 +159,21 @@ class CI_Hooks {
*
* Runs a particular hook
*
- * @access private
- * @param array the hook details
- * @return bool
+ * @param array $data Hook details
+ * @return bool TRUE on success or FALSE on failure
*/
- function _run_hook($data)
+ protected function _run_hook($data)
{
- if ( ! is_array($data))
+ // Closures/lambda functions and array($object, 'method') callables
+ if (is_callable($data))
+ {
+ is_array($data)
+ ? $data[0]->{$data[1]}()
+ : $data();
+
+ return TRUE;
+ }
+ elseif ( ! is_array($data))
{
return FALSE;
}
@@ -157,8 +184,7 @@ class CI_Hooks {
// If the script being called happens to have the same
// hook call within it a loop can happen
-
- if ($this->in_progress == TRUE)
+ if ($this->_in_progress === TRUE)
{
return;
}
@@ -167,7 +193,7 @@ class CI_Hooks {
// Set file path
// -----------------------------------
- if ( ! isset($data['filepath']) OR ! isset($data['filename']))
+ if ( ! isset($data['filepath'], $data['filename']))
{
return FALSE;
}
@@ -179,71 +205,62 @@ class CI_Hooks {
return FALSE;
}
- // -----------------------------------
- // Set class/function name
- // -----------------------------------
+ // Determine and class and/or function names
+ $class = empty($data['class']) ? FALSE : $data['class'];
+ $function = empty($data['function']) ? FALSE : $data['function'];
+ $params = isset($data['params']) ? $data['params'] : '';
- $class = FALSE;
- $function = FALSE;
- $params = '';
-
- if (isset($data['class']) AND $data['class'] != '')
- {
- $class = $data['class'];
- }
-
- if (isset($data['function']))
- {
- $function = $data['function'];
- }
-
- if (isset($data['params']))
- {
- $params = $data['params'];
- }
-
- if ($class === FALSE AND $function === FALSE)
+ if (empty($function))
{
return FALSE;
}
- // -----------------------------------
- // Set the in_progress flag
- // -----------------------------------
+ // Set the _in_progress flag
+ $this->_in_progress = TRUE;
- $this->in_progress = TRUE;
-
- // -----------------------------------
// Call the requested class and/or function
- // -----------------------------------
-
if ($class !== FALSE)
{
- if ( ! class_exists($class))
+ // The object is stored?
+ if (isset($this->_objects[$class]))
{
- require($filepath);
+ if (method_exists($this->_objects[$class], $function))
+ {
+ $this->_objects[$class]->$function($params);
+ }
+ else
+ {
+ return $this->_in_progress = FALSE;
+ }
}
+ else
+ {
+ class_exists($class, FALSE) OR require_once($filepath);
- $HOOK = new $class;
- $HOOK->$function($params);
+ if ( ! class_exists($class, FALSE) OR ! method_exists($class, $function))
+ {
+ return $this->_in_progress = FALSE;
+ }
+
+ // Store the object and execute the method
+ $this->_objects[$class] = new $class();
+ $this->_objects[$class]->$function($params);
+ }
}
else
{
+ function_exists($function) OR require_once($filepath);
+
if ( ! function_exists($function))
{
- require($filepath);
+ return $this->_in_progress = FALSE;
}
$function($params);
}
- $this->in_progress = FALSE;
+ $this->_in_progress = FALSE;
return TRUE;
}
}
-
-// END CI_Hooks class
-
-/* End of file Hooks.php */
-/* Location: ./system/core/Hooks.php */
\ No newline at end of file
diff --git a/src/system/core/Input.php b/src/system/core/Input.php
index 40305745..a7c9ecd0 100644
--- a/src/system/core/Input.php
+++ b/src/system/core/Input.php
@@ -1,20 +1,41 @@
-_allow_get_array = (config_item('allow_get_array') === TRUE);
+ $this->_allow_get_array = (config_item('allow_get_array') === TRUE);
$this->_enable_xss = (config_item('global_xss_filtering') === TRUE);
$this->_enable_csrf = (config_item('csrf_protection') === TRUE);
+ $this->_standardize_newlines = (bool) config_item('standardize_newlines');
- global $SEC;
- $this->security =& $SEC;
+ $this->security =& load_class('Security', 'core');
// Do we need the UTF-8 class?
if (UTF8_ENABLED === TRUE)
{
- global $UNI;
- $this->uni =& $UNI;
+ $this->uni =& load_class('Utf8', 'core');
}
// Sanitize global arrays
$this->_sanitize_globals();
+
+ // CSRF Protection check
+ if ($this->_enable_csrf === TRUE && ! is_cli())
+ {
+ $this->security->csrf_verify();
+ }
+
+ log_message('info', 'Input Class Initialized');
}
// --------------------------------------------------------------------
@@ -109,147 +167,204 @@ class CI_Input {
/**
* Fetch from array
*
- * This is a helper function to retrieve values from global arrays
+ * Internal method used to retrieve values from global arrays.
*
- * @access private
- * @param array
- * @param string
- * @param bool
- * @return string
+ * @param array &$array $_GET, $_POST, $_COOKIE, $_SERVER, etc.
+ * @param mixed $index Index for item to be fetched from $array
+ * @param bool $xss_clean Whether to apply XSS filtering
+ * @return mixed
*/
- function _fetch_from_array(&$array, $index = '', $xss_clean = FALSE)
+ protected function _fetch_from_array(&$array, $index = NULL, $xss_clean = NULL)
{
- if ( ! isset($array[$index]))
+ is_bool($xss_clean) OR $xss_clean = $this->_enable_xss;
+
+ // If $index is NULL, it means that the whole $array is requested
+ isset($index) OR $index = array_keys($array);
+
+ // allow fetching multiple keys at once
+ if (is_array($index))
{
- return FALSE;
+ $output = array();
+ foreach ($index as $key)
+ {
+ $output[$key] = $this->_fetch_from_array($array, $key, $xss_clean);
+ }
+
+ return $output;
}
- if ($xss_clean === TRUE)
+ if (isset($array[$index]))
{
- return $this->security->xss_clean($array[$index]);
+ $value = $array[$index];
+ }
+ elseif (($count = preg_match_all('/(?:^[^\[]+)|\[[^]]*\]/', $index, $matches)) > 1) // Does the index contain array notation
+ {
+ $value = $array;
+ for ($i = 0; $i < $count; $i++)
+ {
+ $key = trim($matches[0][$i], '[]');
+ if ($key === '') // Empty notation will return the value as array
+ {
+ break;
+ }
+
+ if (isset($value[$key]))
+ {
+ $value = $value[$key];
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+ }
+ else
+ {
+ return NULL;
}
- return $array[$index];
+ return ($xss_clean === TRUE)
+ ? $this->security->xss_clean($value)
+ : $value;
}
// --------------------------------------------------------------------
/**
- * Fetch an item from the GET array
- *
- * @access public
- * @param string
- * @param bool
- * @return string
- */
- function get($index = NULL, $xss_clean = FALSE)
+ * Fetch an item from the GET array
+ *
+ * @param mixed $index Index for item to be fetched from $_GET
+ * @param bool $xss_clean Whether to apply XSS filtering
+ * @return mixed
+ */
+ public function get($index = NULL, $xss_clean = NULL)
{
- // Check if a field has been provided
- if ($index === NULL AND ! empty($_GET))
- {
- $get = array();
-
- // loop through the full _GET array
- foreach (array_keys($_GET) as $key)
- {
- $get[$key] = $this->_fetch_from_array($_GET, $key, $xss_clean);
- }
- return $get;
- }
-
return $this->_fetch_from_array($_GET, $index, $xss_clean);
}
// --------------------------------------------------------------------
/**
- * Fetch an item from the POST array
- *
- * @access public
- * @param string
- * @param bool
- * @return string
- */
- function post($index = NULL, $xss_clean = FALSE)
+ * Fetch an item from the POST array
+ *
+ * @param mixed $index Index for item to be fetched from $_POST
+ * @param bool $xss_clean Whether to apply XSS filtering
+ * @return mixed
+ */
+ public function post($index = NULL, $xss_clean = NULL)
{
- // Check if a field has been provided
- if ($index === NULL AND ! empty($_POST))
- {
- $post = array();
-
- // Loop through the full _POST array and return it
- foreach (array_keys($_POST) as $key)
- {
- $post[$key] = $this->_fetch_from_array($_POST, $key, $xss_clean);
- }
- return $post;
- }
-
return $this->_fetch_from_array($_POST, $index, $xss_clean);
}
-
// --------------------------------------------------------------------
/**
- * Fetch an item from either the GET array or the POST
- *
- * @access public
- * @param string The index key
- * @param bool XSS cleaning
- * @return string
- */
- function get_post($index = '', $xss_clean = FALSE)
+ * Fetch an item from POST data with fallback to GET
+ *
+ * @param string $index Index for item to be fetched from $_POST or $_GET
+ * @param bool $xss_clean Whether to apply XSS filtering
+ * @return mixed
+ */
+ public function post_get($index, $xss_clean = NULL)
{
- if ( ! isset($_POST[$index]) )
- {
- return $this->get($index, $xss_clean);
- }
- else
- {
- return $this->post($index, $xss_clean);
- }
+ return isset($_POST[$index])
+ ? $this->post($index, $xss_clean)
+ : $this->get($index, $xss_clean);
}
// --------------------------------------------------------------------
/**
- * Fetch an item from the COOKIE array
- *
- * @access public
- * @param string
- * @param bool
- * @return string
- */
- function cookie($index = '', $xss_clean = FALSE)
+ * Fetch an item from GET data with fallback to POST
+ *
+ * @param string $index Index for item to be fetched from $_GET or $_POST
+ * @param bool $xss_clean Whether to apply XSS filtering
+ * @return mixed
+ */
+ public function get_post($index, $xss_clean = NULL)
+ {
+ return isset($_GET[$index])
+ ? $this->get($index, $xss_clean)
+ : $this->post($index, $xss_clean);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Fetch an item from the COOKIE array
+ *
+ * @param mixed $index Index for item to be fetched from $_COOKIE
+ * @param bool $xss_clean Whether to apply XSS filtering
+ * @return mixed
+ */
+ public function cookie($index = NULL, $xss_clean = NULL)
{
return $this->_fetch_from_array($_COOKIE, $index, $xss_clean);
}
+ // --------------------------------------------------------------------
+
+ /**
+ * Fetch an item from the SERVER array
+ *
+ * @param mixed $index Index for item to be fetched from $_SERVER
+ * @param bool $xss_clean Whether to apply XSS filtering
+ * @return mixed
+ */
+ public function server($index, $xss_clean = NULL)
+ {
+ return $this->_fetch_from_array($_SERVER, $index, $xss_clean);
+ }
+
// ------------------------------------------------------------------------
/**
- * Set cookie
- *
- * Accepts six parameter, or you can submit an associative
- * array in the first parameter containing all the values.
- *
- * @access public
- * @param mixed
- * @param string the value of the cookie
- * @param string the number of seconds until expiration
- * @param string the cookie domain. Usually: .yourdomain.com
- * @param string the cookie path
- * @param string the cookie prefix
- * @param bool true makes the cookie secure
- * @return void
- */
- function set_cookie($name = '', $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = FALSE)
+ * Fetch an item from the php://input stream
+ *
+ * Useful when you need to access PUT, DELETE or PATCH request data.
+ *
+ * @param string $index Index for item to be fetched
+ * @param bool $xss_clean Whether to apply XSS filtering
+ * @return mixed
+ */
+ public function input_stream($index = NULL, $xss_clean = NULL)
+ {
+ // Prior to PHP 5.6, the input stream can only be read once,
+ // so we'll need to check if we have already done that first.
+ if ( ! is_array($this->_input_stream))
+ {
+ // $this->raw_input_stream will trigger __get().
+ parse_str($this->raw_input_stream, $this->_input_stream);
+ is_array($this->_input_stream) OR $this->_input_stream = array();
+ }
+
+ return $this->_fetch_from_array($this->_input_stream, $index, $xss_clean);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Set cookie
+ *
+ * Accepts an arbitrary number of parameters (up to 7) or an associative
+ * array in the first parameter containing all the values.
+ *
+ * @param string|mixed[] $name Cookie name or an array containing parameters
+ * @param string $value Cookie value
+ * @param int $expire Cookie expiration time in seconds
+ * @param string $domain Cookie domain (e.g.: '.yourdomain.com')
+ * @param string $path Cookie path (default: '/')
+ * @param string $prefix Cookie name prefix
+ * @param bool $secure Whether to only transfer cookies via SSL
+ * @param bool $httponly Whether to only makes the cookie accessible via HTTP (no javascript)
+ * @return void
+ */
+ public function set_cookie($name, $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = FALSE, $httponly = FALSE)
{
if (is_array($name))
{
// always leave 'name' in last place, as the loop will break otherwise, due to $$item
- foreach (array('value', 'expire', 'domain', 'path', 'prefix', 'secure', 'name') as $item)
+ foreach (array('value', 'expire', 'domain', 'path', 'prefix', 'secure', 'httponly', 'name') as $item)
{
if (isset($name[$item]))
{
@@ -258,23 +373,31 @@ class CI_Input {
}
}
- if ($prefix == '' AND config_item('cookie_prefix') != '')
+ if ($prefix === '' && config_item('cookie_prefix') !== '')
{
$prefix = config_item('cookie_prefix');
}
- if ($domain == '' AND config_item('cookie_domain') != '')
+
+ if ($domain == '' && config_item('cookie_domain') != '')
{
$domain = config_item('cookie_domain');
}
- if ($path == '/' AND config_item('cookie_path') != '/')
+
+ if ($path === '/' && config_item('cookie_path') !== '/')
{
$path = config_item('cookie_path');
}
- if ($secure == FALSE AND config_item('cookie_secure') != FALSE)
+
+ if ($secure === FALSE && config_item('cookie_secure') === TRUE)
{
$secure = config_item('cookie_secure');
}
+ if ($httponly === FALSE && config_item('cookie_httponly') !== FALSE)
+ {
+ $httponly = config_item('cookie_httponly');
+ }
+
if ( ! is_numeric($expire))
{
$expire = time() - 86500;
@@ -284,31 +407,18 @@ class CI_Input {
$expire = ($expire > 0) ? time() + $expire : 0;
}
- setcookie($prefix.$name, $value, $expire, $path, $domain, $secure);
+ setcookie($prefix.$name, $value, $expire, $path, $domain, $secure, $httponly);
}
// --------------------------------------------------------------------
/**
- * Fetch an item from the SERVER array
- *
- * @access public
- * @param string
- * @param bool
- * @return string
- */
- function server($index = '', $xss_clean = FALSE)
- {
- return $this->_fetch_from_array($_SERVER, $index, $xss_clean);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Fetch the IP Address
- *
- * @return string
- */
+ * Fetch the IP Address
+ *
+ * Determines and validates the visitor's IP address.
+ *
+ * @return string IP address
+ */
public function ip_address()
{
if ($this->ip_address !== FALSE)
@@ -317,25 +427,27 @@ class CI_Input {
}
$proxy_ips = config_item('proxy_ips');
- if ( ! empty($proxy_ips))
+ if ( ! empty($proxy_ips) && ! is_array($proxy_ips))
{
$proxy_ips = explode(',', str_replace(' ', '', $proxy_ips));
+ }
+
+ $this->ip_address = $this->server('REMOTE_ADDR');
+
+ if ($proxy_ips)
+ {
foreach (array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP') as $header)
{
- if (($spoof = $this->server($header)) !== FALSE)
+ if (($spoof = $this->server($header)) !== NULL)
{
// Some proxies typically list the whole chain of IP
// addresses through which the client has reached us.
// e.g. client_ip, proxy_ip1, proxy_ip2, etc.
- if (strpos($spoof, ',') !== FALSE)
- {
- $spoof = explode(',', $spoof, 2);
- $spoof = $spoof[0];
- }
+ sscanf($spoof, '%[^,]', $spoof);
if ( ! $this->valid_ip($spoof))
{
- $spoof = FALSE;
+ $spoof = NULL;
}
else
{
@@ -344,17 +456,92 @@ class CI_Input {
}
}
- $this->ip_address = ($spoof !== FALSE && in_array($_SERVER['REMOTE_ADDR'], $proxy_ips, TRUE))
- ? $spoof : $_SERVER['REMOTE_ADDR'];
- }
- else
- {
- $this->ip_address = $_SERVER['REMOTE_ADDR'];
+ if ($spoof)
+ {
+ for ($i = 0, $c = count($proxy_ips); $i < $c; $i++)
+ {
+ // Check if we have an IP address or a subnet
+ if (strpos($proxy_ips[$i], '/') === FALSE)
+ {
+ // An IP address (and not a subnet) is specified.
+ // We can compare right away.
+ if ($proxy_ips[$i] === $this->ip_address)
+ {
+ $this->ip_address = $spoof;
+ break;
+ }
+
+ continue;
+ }
+
+ // We have a subnet ... now the heavy lifting begins
+ isset($separator) OR $separator = $this->valid_ip($this->ip_address, 'ipv6') ? ':' : '.';
+
+ // If the proxy entry doesn't match the IP protocol - skip it
+ if (strpos($proxy_ips[$i], $separator) === FALSE)
+ {
+ continue;
+ }
+
+ // Convert the REMOTE_ADDR IP address to binary, if needed
+ if ( ! isset($ip, $sprintf))
+ {
+ if ($separator === ':')
+ {
+ // Make sure we're have the "full" IPv6 format
+ $ip = explode(':',
+ str_replace('::',
+ str_repeat(':', 9 - substr_count($this->ip_address, ':')),
+ $this->ip_address
+ )
+ );
+
+ for ($j = 0; $j < 8; $j++)
+ {
+ $ip[$j] = intval($ip[$j], 16);
+ }
+
+ $sprintf = '%016b%016b%016b%016b%016b%016b%016b%016b';
+ }
+ else
+ {
+ $ip = explode('.', $this->ip_address);
+ $sprintf = '%08b%08b%08b%08b';
+ }
+
+ $ip = vsprintf($sprintf, $ip);
+ }
+
+ // Split the netmask length off the network address
+ sscanf($proxy_ips[$i], '%[^/]/%d', $netaddr, $masklen);
+
+ // Again, an IPv6 address is most likely in a compressed form
+ if ($separator === ':')
+ {
+ $netaddr = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($netaddr, ':')), $netaddr));
+ for ($i = 0; $i < 8; $i++)
+ {
+ $netaddr[$i] = intval($netaddr[$i], 16);
+ }
+ }
+ else
+ {
+ $netaddr = explode('.', $netaddr);
+ }
+
+ // Convert to binary and finally compare
+ if (strncmp($ip, vsprintf($sprintf, $netaddr), $masklen) === 0)
+ {
+ $this->ip_address = $spoof;
+ break;
+ }
+ }
+ }
}
if ( ! $this->valid_ip($this->ip_address))
{
- $this->ip_address = '0.0.0.0';
+ return $this->ip_address = '0.0.0.0';
}
return $this->ip_address;
@@ -363,256 +550,72 @@ class CI_Input {
// --------------------------------------------------------------------
/**
- * Validate IP Address
- *
- * @access public
- * @param string
- * @param string ipv4 or ipv6
- * @return bool
- */
+ * Validate IP Address
+ *
+ * @param string $ip IP address
+ * @param string $which IP protocol: 'ipv4' or 'ipv6'
+ * @return bool
+ */
public function valid_ip($ip, $which = '')
{
- $which = strtolower($which);
-
- // First check if filter_var is available
- if (is_callable('filter_var'))
+ switch (strtolower($which))
{
- switch ($which) {
- case 'ipv4':
- $flag = FILTER_FLAG_IPV4;
- break;
- case 'ipv6':
- $flag = FILTER_FLAG_IPV6;
- break;
- default:
- $flag = '';
- break;
- }
-
- return (bool) filter_var($ip, FILTER_VALIDATE_IP, $flag);
+ case 'ipv4':
+ $which = FILTER_FLAG_IPV4;
+ break;
+ case 'ipv6':
+ $which = FILTER_FLAG_IPV6;
+ break;
+ default:
+ $which = NULL;
+ break;
}
- if ($which !== 'ipv6' && $which !== 'ipv4')
- {
- if (strpos($ip, ':') !== FALSE)
- {
- $which = 'ipv6';
- }
- elseif (strpos($ip, '.') !== FALSE)
- {
- $which = 'ipv4';
- }
- else
- {
- return FALSE;
- }
- }
-
- $func = '_valid_'.$which;
- return $this->$func($ip);
+ return (bool) filter_var($ip, FILTER_VALIDATE_IP, $which);
}
// --------------------------------------------------------------------
/**
- * Validate IPv4 Address
- *
- * Updated version suggested by Geert De Deckere
- *
- * @access protected
- * @param string
- * @return bool
- */
- protected function _valid_ipv4($ip)
+ * Fetch User Agent string
+ *
+ * @return string|null User Agent string or NULL if it doesn't exist
+ */
+ public function user_agent($xss_clean = NULL)
{
- $ip_segments = explode('.', $ip);
-
- // Always 4 segments needed
- if (count($ip_segments) !== 4)
- {
- return FALSE;
- }
- // IP can not start with 0
- if ($ip_segments[0][0] == '0')
- {
- return FALSE;
- }
-
- // Check each segment
- foreach ($ip_segments as $segment)
- {
- // IP segments must be digits and can not be
- // longer than 3 digits or greater then 255
- if ($segment == '' OR preg_match("/[^0-9]/", $segment) OR $segment > 255 OR strlen($segment) > 3)
- {
- return FALSE;
- }
- }
-
- return TRUE;
+ return $this->_fetch_from_array($_SERVER, 'HTTP_USER_AGENT', $xss_clean);
}
// --------------------------------------------------------------------
/**
- * Validate IPv6 Address
- *
- * @access protected
- * @param string
- * @return bool
- */
- protected function _valid_ipv6($str)
+ * Sanitize Globals
+ *
+ * Internal method serving for the following purposes:
+ *
+ * - Unsets $_GET data, if query strings are not enabled
+ * - Cleans POST, COOKIE and SERVER data
+ * - Standardizes newline characters to PHP_EOL
+ *
+ * @return void
+ */
+ protected function _sanitize_globals()
{
- // 8 groups, separated by :
- // 0-ffff per group
- // one set of consecutive 0 groups can be collapsed to ::
-
- $groups = 8;
- $collapsed = FALSE;
-
- $chunks = array_filter(
- preg_split('/(:{1,2})/', $str, NULL, PREG_SPLIT_DELIM_CAPTURE)
- );
-
- // Rule out easy nonsense
- if (current($chunks) == ':' OR end($chunks) == ':')
- {
- return FALSE;
- }
-
- // PHP supports IPv4-mapped IPv6 addresses, so we'll expect those as well
- if (strpos(end($chunks), '.') !== FALSE)
- {
- $ipv4 = array_pop($chunks);
-
- if ( ! $this->_valid_ipv4($ipv4))
- {
- return FALSE;
- }
-
- $groups--;
- }
-
- while ($seg = array_pop($chunks))
- {
- if ($seg[0] == ':')
- {
- if (--$groups == 0)
- {
- return FALSE; // too many groups
- }
-
- if (strlen($seg) > 2)
- {
- return FALSE; // long separator
- }
-
- if ($seg == '::')
- {
- if ($collapsed)
- {
- return FALSE; // multiple collapsed
- }
-
- $collapsed = TRUE;
- }
- }
- elseif (preg_match("/[^0-9a-f]/i", $seg) OR strlen($seg) > 4)
- {
- return FALSE; // invalid segment
- }
- }
-
- return $collapsed OR $groups == 1;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * User Agent
- *
- * @access public
- * @return string
- */
- function user_agent()
- {
- if ($this->user_agent !== FALSE)
- {
- return $this->user_agent;
- }
-
- $this->user_agent = ( ! isset($_SERVER['HTTP_USER_AGENT'])) ? FALSE : $_SERVER['HTTP_USER_AGENT'];
-
- return $this->user_agent;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Sanitize Globals
- *
- * This function does the following:
- *
- * Unsets $_GET data (if query strings are not enabled)
- *
- * Unsets all globals if register_globals is enabled
- *
- * Standardizes newline characters to \n
- *
- * @access private
- * @return void
- */
- function _sanitize_globals()
- {
- // It would be "wrong" to unset any of these GLOBALS.
- $protected = array('_SERVER', '_GET', '_POST', '_FILES', '_REQUEST',
- '_SESSION', '_ENV', 'GLOBALS', 'HTTP_RAW_POST_DATA',
- 'system_folder', 'application_folder', 'BM', 'EXT',
- 'CFG', 'URI', 'RTR', 'OUT', 'IN');
-
- // Unset globals for securiy.
- // This is effectively the same as register_globals = off
- foreach (array($_GET, $_POST, $_COOKIE) as $global)
- {
- if ( ! is_array($global))
- {
- if ( ! in_array($global, $protected))
- {
- global $$global;
- $$global = NULL;
- }
- }
- else
- {
- foreach ($global as $key => $val)
- {
- if ( ! in_array($key, $protected))
- {
- global $$key;
- $$key = NULL;
- }
- }
- }
- }
-
// Is $_GET data allowed? If not we'll set the $_GET to an empty array
- if ($this->_allow_get_array == FALSE)
+ if ($this->_allow_get_array === FALSE)
{
$_GET = array();
}
- else
+ elseif (is_array($_GET))
{
- if (is_array($_GET) AND count($_GET) > 0)
+ foreach ($_GET as $key => $val)
{
- foreach ($_GET as $key => $val)
- {
- $_GET[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
- }
+ $_GET[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
}
}
// Clean $_POST Data
- if (is_array($_POST) AND count($_POST) > 0)
+ if (is_array($_POST))
{
foreach ($_POST as $key => $val)
{
@@ -621,72 +624,57 @@ class CI_Input {
}
// Clean $_COOKIE Data
- if (is_array($_COOKIE) AND count($_COOKIE) > 0)
+ if (is_array($_COOKIE))
{
// Also get rid of specially treated cookies that might be set by a server
// or silly application, that are of no use to a CI application anyway
// but that when present will trip our 'Disallowed Key Characters' alarm
// http://www.ietf.org/rfc/rfc2109.txt
// note that the key names below are single quoted strings, and are not PHP variables
- unset($_COOKIE['$Version']);
- unset($_COOKIE['$Path']);
- unset($_COOKIE['$Domain']);
-
- // Work-around for PHP bug #66827 (https://bugs.php.net/bug.php?id=66827)
- //
- // The session ID sanitizer doesn't check for the value type and blindly does
- // an implicit cast to string, which triggers an 'Array to string' E_NOTICE.
- $sess_cookie_name = config_item('cookie_prefix').config_item('sess_cookie_name');
- if (isset($_COOKIE[$sess_cookie_name]) && ! is_string($_COOKIE[$sess_cookie_name]))
- {
- unset($_COOKIE[$sess_cookie_name]);
- }
+ unset(
+ $_COOKIE['$Version'],
+ $_COOKIE['$Path'],
+ $_COOKIE['$Domain']
+ );
foreach ($_COOKIE as $key => $val)
{
- // _clean_input_data() has been reported to break encrypted cookies
- if ($key === $sess_cookie_name && config_item('sess_encrypt_cookie'))
+ if (($cookie_key = $this->_clean_input_keys($key)) !== FALSE)
{
- continue;
+ $_COOKIE[$cookie_key] = $this->_clean_input_data($val);
+ }
+ else
+ {
+ unset($_COOKIE[$key]);
}
-
- $_COOKIE[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
}
}
// Sanitize PHP_SELF
$_SERVER['PHP_SELF'] = strip_tags($_SERVER['PHP_SELF']);
-
- // CSRF Protection check on HTTP requests
- if ($this->_enable_csrf == TRUE && ! $this->is_cli_request())
- {
- $this->security->csrf_verify();
- }
-
- log_message('debug', "Global POST and COOKIE data sanitized");
+ log_message('debug', 'Global POST, GET and COOKIE data sanitized');
}
// --------------------------------------------------------------------
/**
- * Clean Input Data
- *
- * This is a helper function. It escapes data and
- * standardizes newline characters to \n
- *
- * @access private
- * @param string
- * @return string
- */
- function _clean_input_data($str)
+ * Clean Input Data
+ *
+ * Internal method that aids in escaping data and
+ * standardizing newline characters to PHP_EOL.
+ *
+ * @param string|string[] $str Input string(s)
+ * @return string
+ */
+ protected function _clean_input_data($str)
{
if (is_array($str))
{
$new_array = array();
- foreach ($str as $key => $val)
+ foreach (array_keys($str) as $key)
{
- $new_array[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
+ $new_array[$this->_clean_input_keys($key)] = $this->_clean_input_data($str[$key]);
}
return $new_array;
}
@@ -694,7 +682,7 @@ class CI_Input {
/* We strip slashes if magic quotes is on to keep things consistent
NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and
- it will probably not exist in future versions at all.
+ it will probably not exist in future versions at all.
*/
if ( ! is_php('5.4') && get_magic_quotes_gpc())
{
@@ -708,21 +696,12 @@ class CI_Input {
}
// Remove control characters
- $str = remove_invisible_characters($str);
-
- // Should we filter the input data?
- if ($this->_enable_xss === TRUE)
- {
- $str = $this->security->xss_clean($str);
- }
+ $str = remove_invisible_characters($str, FALSE);
// Standardize newlines if needed
- if ($this->_standardize_newlines == TRUE)
+ if ($this->_standardize_newlines === TRUE)
{
- if (strpos($str, "\r") !== FALSE)
- {
- $str = str_replace(array("\r\n", "\r", "\r\n\n"), PHP_EOL, $str);
- }
+ return preg_replace('/(?:\r\n|[\r\n])/', PHP_EOL, $str);
}
return $str;
@@ -731,27 +710,38 @@ class CI_Input {
// --------------------------------------------------------------------
/**
- * Clean Keys
- *
- * This is a helper function. To prevent malicious users
- * from trying to exploit keys we make sure that keys are
- * only named with alpha-numeric text and a few other items.
- *
- * @access private
- * @param string
- * @return string
- */
- function _clean_input_keys($str)
+ * Clean Keys
+ *
+ * Internal method that helps to prevent malicious users
+ * from trying to exploit keys we make sure that keys are
+ * only named with alpha-numeric text and a few other items.
+ *
+ * @param string $str Input string
+ * @param bool $fatal Whether to terminate script exection
+ * or to return FALSE if an invalid
+ * key is encountered
+ * @return string|bool
+ */
+ protected function _clean_input_keys($str, $fatal = TRUE)
{
- if ( ! preg_match("/^[a-z0-9:_\/-]+$/i", $str))
+ if ( ! preg_match('/^[a-z0-9:_\/|-]+$/i', $str))
{
- exit('Disallowed Key Characters.');
+ if ($fatal === TRUE)
+ {
+ return FALSE;
+ }
+ else
+ {
+ set_status_header(503);
+ echo 'Disallowed Key Characters.';
+ exit(7); // EXIT_USER_INPUT
+ }
}
// Clean UTF-8 if supported
if (UTF8_ENABLED === TRUE)
{
- $str = $this->uni->clean_string($str);
+ return $this->uni->clean_string($str);
}
return $str;
@@ -762,42 +752,37 @@ class CI_Input {
/**
* Request Headers
*
- * In Apache, you can simply call apache_request_headers(), however for
- * people running other webservers the function is undefined.
- *
- * @param bool XSS cleaning
- *
- * @return array
+ * @param bool $xss_clean Whether to apply XSS filtering
+ * @return array
*/
public function request_headers($xss_clean = FALSE)
{
- // Look at Apache go!
+ // If header is already defined, return it immediately
+ if ( ! empty($this->headers))
+ {
+ return $this->headers;
+ }
+
+ // In Apache, you can simply call apache_request_headers()
if (function_exists('apache_request_headers'))
{
- $headers = apache_request_headers();
+ return $this->headers = apache_request_headers();
}
- else
- {
- $headers['Content-Type'] = (isset($_SERVER['CONTENT_TYPE'])) ? $_SERVER['CONTENT_TYPE'] : @getenv('CONTENT_TYPE');
- foreach ($_SERVER as $key => $val)
+ $this->headers['Content-Type'] = isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : @getenv('CONTENT_TYPE');
+
+ foreach ($_SERVER as $key => $val)
+ {
+ if (sscanf($key, 'HTTP_%s', $header) === 1)
{
- if (strncmp($key, 'HTTP_', 5) === 0)
- {
- $headers[substr($key, 5)] = $this->_fetch_from_array($_SERVER, $key, $xss_clean);
- }
+ // take SOME_HEADER and turn it into Some-Header
+ $header = str_replace('_', ' ', strtolower($header));
+ $header = str_replace(' ', '-', ucwords($header));
+
+ $this->headers[$header] = $this->_fetch_from_array($_SERVER, $key, $xss_clean);
}
}
- // take SOME_HEADER and turn it into Some-Header
- foreach ($headers as $key => $val)
- {
- $key = str_replace('_', ' ', strtolower($key));
- $key = str_replace(' ', '-', ucwords($key));
-
- $this->headers[$key] = $val;
- }
-
return $this->headers;
}
@@ -808,59 +793,103 @@ class CI_Input {
*
* Returns the value of a single member of the headers class member
*
- * @param string array key for $this->headers
- * @param boolean XSS Clean or not
- * @return mixed FALSE on failure, string on success
+ * @param string $index Header name
+ * @param bool $xss_clean Whether to apply XSS filtering
+ * @return string|null The requested header on success or NULL on failure
*/
public function get_request_header($index, $xss_clean = FALSE)
{
- if (empty($this->headers))
+ static $headers;
+
+ if ( ! isset($headers))
{
- $this->request_headers();
+ empty($this->headers) && $this->request_headers();
+ foreach ($this->headers as $key => $value)
+ {
+ $headers[strtolower($key)] = $value;
+ }
}
- if ( ! isset($this->headers[$index]))
+ $index = strtolower($index);
+
+ if ( ! isset($headers[$index]))
{
- return FALSE;
+ return NULL;
}
- if ($xss_clean === TRUE)
- {
- return $this->security->xss_clean($this->headers[$index]);
- }
-
- return $this->headers[$index];
+ return ($xss_clean === TRUE)
+ ? $this->security->xss_clean($headers[$index])
+ : $headers[$index];
}
// --------------------------------------------------------------------
/**
- * Is ajax Request?
+ * Is AJAX request?
*
- * Test to see if a request contains the HTTP_X_REQUESTED_WITH header
- *
- * @return boolean
- */
- public function is_ajax_request()
- {
- return ($this->server('HTTP_X_REQUESTED_WITH') === 'XMLHttpRequest');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Is cli Request?
- *
- * Test to see if a request was made from the command line
+ * Test to see if a request contains the HTTP_X_REQUESTED_WITH header.
*
* @return bool
*/
+ public function is_ajax_request()
+ {
+ return ( ! empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Is CLI request?
+ *
+ * Test to see if a request was made from the command line.
+ *
+ * @deprecated 3.0.0 Use is_cli() instead
+ * @return bool
+ */
public function is_cli_request()
{
- return (php_sapi_name() === 'cli' OR defined('STDIN'));
+ return is_cli();
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Get Request Method
+ *
+ * Return the request method
+ *
+ * @param bool $upper Whether to return in upper or lower case
+ * (default: FALSE)
+ * @return string
+ */
+ public function method($upper = FALSE)
+ {
+ return ($upper)
+ ? strtoupper($this->server('REQUEST_METHOD'))
+ : strtolower($this->server('REQUEST_METHOD'));
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Magic __get()
+ *
+ * Allows read access to protected properties
+ *
+ * @param string $name
+ * @return mixed
+ */
+ public function __get($name)
+ {
+ if ($name === 'raw_input_stream')
+ {
+ isset($this->_raw_input_stream) OR $this->_raw_input_stream = file_get_contents('php://input');
+ return $this->_raw_input_stream;
+ }
+ elseif ($name === 'ip_address')
+ {
+ return $this->ip_address;
+ }
}
}
-
-/* End of file Input.php */
-/* Location: ./system/core/Input.php */
\ No newline at end of file
diff --git a/src/system/core/Lang.php b/src/system/core/Lang.php
index 28497034..1fcff078 100644
--- a/src/system/core/Lang.php
+++ b/src/system/core/Lang.php
@@ -1,20 +1,41 @@
-load($value, $idiom, $return, $add_suffix, $alt_path);
+ }
+
+ return;
+ }
+
$langfile = str_replace('.php', '', $langfile);
- if ($add_suffix == TRUE)
+ if ($add_suffix === TRUE)
{
- $langfile = str_replace('_lang.', '', $langfile).'_lang';
+ $langfile = preg_replace('/_lang$/', '', $langfile).'_lang';
}
$langfile .= '.php';
- if (in_array($langfile, $this->is_loaded, TRUE))
+ if (empty($idiom) OR ! preg_match('/^[a-z_-]+$/i', $idiom))
+ {
+ $config =& get_config();
+ $idiom = empty($config['language']) ? 'english' : $config['language'];
+ }
+
+ if ($return === FALSE && isset($this->is_loaded[$langfile]) && $this->is_loaded[$langfile] === $idiom)
{
return;
}
- $config =& get_config();
-
- if ($idiom == '')
+ // Load the base file, so any others found can override it
+ $basepath = BASEPATH.'language/'.$idiom.'/'.$langfile;
+ if (($found = file_exists($basepath)) === TRUE)
{
- $deft_lang = ( ! isset($config['language'])) ? 'english' : $config['language'];
- $idiom = ($deft_lang == '') ? 'english' : $deft_lang;
+ include($basepath);
}
- // Determine where the language file is and load it
- if ($alt_path != '' && file_exists($alt_path.'language/'.$idiom.'/'.$langfile))
+ // Do we have an alternative path to look in?
+ if ($alt_path !== '')
{
- include($alt_path.'language/'.$idiom.'/'.$langfile);
+ $alt_path .= 'language/'.$idiom.'/'.$langfile;
+ if (file_exists($alt_path))
+ {
+ include($alt_path);
+ $found = TRUE;
+ }
}
else
{
- $found = FALSE;
-
foreach (get_instance()->load->get_package_paths(TRUE) as $package_path)
{
- if (file_exists($package_path.'language/'.$idiom.'/'.$langfile))
+ $package_path .= 'language/'.$idiom.'/'.$langfile;
+ if ($basepath !== $package_path && file_exists($package_path))
{
- include($package_path.'language/'.$idiom.'/'.$langfile);
+ include($package_path);
$found = TRUE;
break;
}
}
-
- if ($found !== TRUE)
- {
- show_error('Unable to load the requested language file: language/'.$idiom.'/'.$langfile);
- }
}
+ if ($found !== TRUE)
+ {
+ show_error('Unable to load the requested language file: language/'.$idiom.'/'.$langfile);
+ }
- if ( ! isset($lang))
+ if ( ! isset($lang) OR ! is_array($lang))
{
log_message('error', 'Language file contains no data: language/'.$idiom.'/'.$langfile);
+
+ if ($return === TRUE)
+ {
+ return array();
+ }
return;
}
- if ($return == TRUE)
+ if ($return === TRUE)
{
return $lang;
}
- $this->is_loaded[] = $langfile;
+ $this->is_loaded[$langfile] = $idiom;
$this->language = array_merge($this->language, $lang);
- unset($lang);
- log_message('debug', 'Language file loaded: language/'.$idiom.'/'.$langfile);
+ log_message('info', 'Language file loaded: language/'.$idiom.'/'.$langfile);
return TRUE;
}
// --------------------------------------------------------------------
/**
- * Fetch a single line of text from the language array
+ * Language line
*
- * @access public
- * @param string $line the language line
- * @return string
+ * Fetches a single line of text from the language array
+ *
+ * @param string $line Language line key
+ * @param bool $log_errors Whether to log an error message if the line is not found
+ * @return string Translation
*/
- function line($line = '')
+ public function line($line, $log_errors = TRUE)
{
- $value = ($line == '' OR ! isset($this->language[$line])) ? FALSE : $this->language[$line];
+ $value = isset($this->language[$line]) ? $this->language[$line] : FALSE;
// Because killer robots like unicorns!
- if ($value === FALSE)
+ if ($value === FALSE && $log_errors === TRUE)
{
log_message('error', 'Could not find the language line "'.$line.'"');
}
@@ -155,7 +201,3 @@ class CI_Lang {
}
}
-// END Language Class
-
-/* End of file Lang.php */
-/* Location: ./system/core/Lang.php */
diff --git a/src/system/core/Loader.php b/src/system/core/Loader.php
index 40090db6..c742ae71 100644
--- a/src/system/core/Loader.php
+++ b/src/system/core/Loader.php
@@ -1,31 +1,52 @@
- TRUE);
+
/**
* List of paths to load libraries from
*
- * @var array
- * @access protected
+ * @var array
*/
- protected $_ci_library_paths = array();
+ protected $_ci_library_paths = array(APPPATH, BASEPATH);
+
/**
* List of paths to load models from
*
- * @var array
- * @access protected
+ * @var array
*/
- protected $_ci_model_paths = array();
+ protected $_ci_model_paths = array(APPPATH);
+
/**
* List of paths to load helpers from
*
- * @var array
- * @access protected
+ * @var array
*/
- protected $_ci_helper_paths = array();
- /**
- * List of loaded base classes
- * Set by the controller class
- *
- * @var array
- * @access protected
- */
- protected $_base_classes = array(); // Set by the controller class
+ protected $_ci_helper_paths = array(APPPATH, BASEPATH);
+
/**
* List of cached variables
*
- * @var array
- * @access protected
+ * @var array
*/
- protected $_ci_cached_vars = array();
+ protected $_ci_cached_vars = array();
+
/**
* List of loaded classes
*
- * @var array
- * @access protected
+ * @var array
*/
- protected $_ci_classes = array();
- /**
- * List of loaded files
- *
- * @var array
- * @access protected
- */
- protected $_ci_loaded_files = array();
+ protected $_ci_classes = array();
+
/**
* List of loaded models
*
- * @var array
- * @access protected
+ * @var array
*/
- protected $_ci_models = array();
+ protected $_ci_models = array();
+
/**
* List of loaded helpers
*
- * @var array
- * @access protected
+ * @var array
*/
- protected $_ci_helpers = array();
+ protected $_ci_helpers = array();
+
/**
* List of class name mappings
*
- * @var array
- * @access protected
+ * @var array
*/
- protected $_ci_varmap = array('unit_test' => 'unit',
- 'user_agent' => 'agent');
+ protected $_ci_varmap = array(
+ 'unit_test' => 'unit',
+ 'user_agent' => 'agent'
+ );
+
+ // --------------------------------------------------------------------
/**
- * Constructor
+ * Class constructor
*
- * Sets the path to the view files and gets the initial output buffering level
+ * Sets component load paths, gets the initial output buffering level.
+ *
+ * @return void
*/
public function __construct()
{
- $this->_ci_ob_level = ob_get_level();
- $this->_ci_library_paths = array(APPPATH, BASEPATH);
- $this->_ci_helper_paths = array(APPPATH, BASEPATH);
- $this->_ci_model_paths = array(APPPATH);
- $this->_ci_view_paths = array(APPPATH.'views/' => TRUE);
+ $this->_ci_ob_level = ob_get_level();
+ $this->_ci_classes =& is_loaded();
- log_message('debug', "Loader Class Initialized");
+ log_message('info', 'Loader Class Initialized');
}
// --------------------------------------------------------------------
/**
- * Initialize the Loader
+ * Initializer
*
- * This method is called once in CI_Controller.
- *
- * @param array
- * @return object
+ * @todo Figure out a way to move this to the constructor
+ * without breaking *package_path*() methods.
+ * @uses CI_Loader::_ci_autoloader()
+ * @used-by CI_Controller::__construct()
+ * @return void
*/
public function initialize()
{
- $this->_ci_classes = array();
- $this->_ci_loaded_files = array();
- $this->_ci_models = array();
- $this->_base_classes =& is_loaded();
-
$this->_ci_autoloader();
-
- return $this;
}
// --------------------------------------------------------------------
@@ -160,61 +162,61 @@ class CI_Loader {
/**
* Is Loaded
*
- * A utility function to test if a class is in the self::$_ci_classes array.
- * This function returns the object name if the class tested for is loaded,
- * and returns FALSE if it isn't.
+ * A utility method to test if a class is in the self::$_ci_classes array.
*
- * It is mainly used in the form_helper -> _get_validation_object()
+ * @used-by Mainly used by Form Helper function _get_validation_object().
*
- * @param string class being checked for
- * @return mixed class object name on the CI SuperObject or FALSE
+ * @param string $class Class name to check for
+ * @return string|bool Class object name if loaded or FALSE
*/
public function is_loaded($class)
{
- if (isset($this->_ci_classes[$class]))
- {
- return $this->_ci_classes[$class];
- }
-
- return FALSE;
+ return array_search(ucfirst($class), $this->_ci_classes, TRUE);
}
// --------------------------------------------------------------------
/**
- * Class Loader
+ * Library Loader
*
- * This function lets users load and instantiate classes.
- * It is designed to be called from a user's app controllers.
+ * Loads and instantiates libraries.
+ * Designed to be called from application controllers.
*
- * @param string the name of the class
- * @param mixed the optional parameters
- * @param string an optional object name
- * @return void
+ * @param string $library Library name
+ * @param array $params Optional parameters to pass to the library class constructor
+ * @param string $object_name An optional object name to assign to
+ * @return object
*/
- public function library($library = '', $params = NULL, $object_name = NULL)
+ public function library($library, $params = NULL, $object_name = NULL)
{
- if (is_array($library))
+ if (empty($library))
{
- foreach ($library as $class)
+ return $this;
+ }
+ elseif (is_array($library))
+ {
+ foreach ($library as $key => $value)
{
- $this->library($class, $params);
+ if (is_int($key))
+ {
+ $this->library($value, $params);
+ }
+ else
+ {
+ $this->library($key, $params, $value);
+ }
}
- return;
+ return $this;
}
- if ($library == '' OR isset($this->_base_classes[$library]))
- {
- return FALSE;
- }
-
- if ( ! is_null($params) && ! is_array($params))
+ if ($params !== NULL && ! is_array($params))
{
$params = NULL;
}
- $this->_ci_load_class($library, $params, $object_name);
+ $this->_ci_load_library($library, $params, $object_name);
+ return $this;
}
// --------------------------------------------------------------------
@@ -222,27 +224,27 @@ class CI_Loader {
/**
* Model Loader
*
- * This function lets users load and instantiate models.
+ * Loads and instantiates models.
*
- * @param string the name of the class
- * @param string name for the model
- * @param bool database connection
- * @return void
+ * @param string $model Model name
+ * @param string $name An optional object name to assign to
+ * @param bool $db_conn An optional database connection configuration to initialize
+ * @return object
*/
public function model($model, $name = '', $db_conn = FALSE)
{
- if (is_array($model))
+ if (empty($model))
{
- foreach ($model as $babe)
- {
- $this->model($babe);
- }
- return;
+ return $this;
}
-
- if ($model == '')
+ elseif (is_array($model))
{
- return;
+ foreach ($model as $key => $value)
+ {
+ is_int($key) ? $this->model($value, '', $db_conn) : $this->model($key, $value, $db_conn);
+ }
+
+ return $this;
}
$path = '';
@@ -251,64 +253,105 @@ class CI_Loader {
if (($last_slash = strrpos($model, '/')) !== FALSE)
{
// The path is in front of the last slash
- $path = substr($model, 0, $last_slash + 1);
+ $path = substr($model, 0, ++$last_slash);
// And the model name behind it
- $model = substr($model, $last_slash + 1);
+ $model = substr($model, $last_slash);
}
- if ($name == '')
+ if (empty($name))
{
$name = $model;
}
if (in_array($name, $this->_ci_models, TRUE))
{
- return;
+ return $this;
}
$CI =& get_instance();
if (isset($CI->$name))
{
- show_error('The model name you are loading is the name of a resource that is already being used: '.$name);
+ throw new RuntimeException('The model name you are loading is the name of a resource that is already being used: '.$name);
}
- $model = strtolower($model);
-
- foreach ($this->_ci_model_paths as $mod_path)
+ if ($db_conn !== FALSE && ! class_exists('CI_DB', FALSE))
{
- if ( ! file_exists($mod_path.'models/'.$path.$model.'.php'))
+ if ($db_conn === TRUE)
{
- continue;
+ $db_conn = '';
}
- if ($db_conn !== FALSE AND ! class_exists('CI_DB'))
+ $this->database($db_conn, FALSE, TRUE);
+ }
+
+ // Note: All of the code under this condition used to be just:
+ //
+ // load_class('Model', 'core');
+ //
+ // However, load_class() instantiates classes
+ // to cache them for later use and that prevents
+ // MY_Model from being an abstract class and is
+ // sub-optimal otherwise anyway.
+ if ( ! class_exists('CI_Model', FALSE))
+ {
+ $app_path = APPPATH.'core'.DIRECTORY_SEPARATOR;
+ if (file_exists($app_path.'Model.php'))
{
- if ($db_conn === TRUE)
+ require_once($app_path.'Model.php');
+ if ( ! class_exists('CI_Model', FALSE))
{
- $db_conn = '';
+ throw new RuntimeException($app_path."Model.php exists, but doesn't declare class CI_Model");
+ }
+ }
+ elseif ( ! class_exists('CI_Model', FALSE))
+ {
+ require_once(BASEPATH.'core'.DIRECTORY_SEPARATOR.'Model.php');
+ }
+
+ $class = config_item('subclass_prefix').'Model';
+ if (file_exists($app_path.$class.'.php'))
+ {
+ require_once($app_path.$class.'.php');
+ if ( ! class_exists($class, FALSE))
+ {
+ throw new RuntimeException($app_path.$class.".php exists, but doesn't declare class ".$class);
+ }
+ }
+ }
+
+ $model = ucfirst($model);
+ if ( ! class_exists($model, FALSE))
+ {
+ foreach ($this->_ci_model_paths as $mod_path)
+ {
+ if ( ! file_exists($mod_path.'models/'.$path.$model.'.php'))
+ {
+ continue;
}
- $CI->load->database($db_conn, FALSE, TRUE);
+ require_once($mod_path.'models/'.$path.$model.'.php');
+ if ( ! class_exists($model, FALSE))
+ {
+ throw new RuntimeException($mod_path."models/".$path.$model.".php exists, but doesn't declare class ".$model);
+ }
+
+ break;
}
- if ( ! class_exists('CI_Model'))
+ if ( ! class_exists($model, FALSE))
{
- load_class('Model', 'core');
+ throw new RuntimeException('Unable to locate the model you have specified: '.$model);
}
-
- require_once($mod_path.'models/'.$path.$model.'.php');
-
- $model = ucfirst($model);
-
- $CI->$name = new $model();
-
- $this->_ci_models[] = $name;
- return;
+ }
+ elseif ( ! is_subclass_of($model, 'CI_Model'))
+ {
+ throw new RuntimeException("Class ".$model." already exists and doesn't extend CI_Model");
}
- // couldn't find the model
- show_error('Unable to locate the model you have specified: '.$model);
+ $this->_ci_models[] = $name;
+ $CI->$name = new $model();
+ return $this;
}
// --------------------------------------------------------------------
@@ -316,18 +359,21 @@ class CI_Loader {
/**
* Database Loader
*
- * @param string the DB credentials
- * @param bool whether to return the DB object
- * @param bool whether to enable active record (this allows us to override the config setting)
- * @return object
+ * @param mixed $params Database configuration options
+ * @param bool $return Whether to return the database object
+ * @param bool $query_builder Whether to enable Query Builder
+ * (overrides the configuration setting)
+ *
+ * @return object|bool Database object if $return is set to TRUE,
+ * FALSE on failure, CI_Loader instance in any other case
*/
- public function database($params = '', $return = FALSE, $active_record = NULL)
+ public function database($params = '', $return = FALSE, $query_builder = NULL)
{
// Grab the super object
$CI =& get_instance();
// Do we even need to load the database class?
- if (class_exists('CI_DB') AND $return == FALSE AND $active_record == NULL AND isset($CI->db) AND is_object($CI->db))
+ if ($return === FALSE && $query_builder === NULL && isset($CI->db) && is_object($CI->db) && ! empty($CI->db->conn_id))
{
return FALSE;
}
@@ -336,42 +382,48 @@ class CI_Loader {
if ($return === TRUE)
{
- return DB($params, $active_record);
+ return DB($params, $query_builder);
}
- // Initialize the db variable. Needed to prevent
+ // Initialize the db variable. Needed to prevent
// reference errors with some configurations
$CI->db = '';
// Load the DB class
- $CI->db =& DB($params, $active_record);
+ $CI->db =& DB($params, $query_builder);
+ return $this;
}
// --------------------------------------------------------------------
/**
- * Load the Utilities Class
+ * Load the Database Utilities Class
*
- * @return string
+ * @param object $db Database object
+ * @param bool $return Whether to return the DB Utilities class object or not
+ * @return object
*/
- public function dbutil()
+ public function dbutil($db = NULL, $return = FALSE)
{
- if ( ! class_exists('CI_DB'))
- {
- $this->database();
- }
-
$CI =& get_instance();
- // for backwards compatibility, load dbforge so we can extend dbutils off it
- // this use is deprecated and strongly discouraged
- $CI->load->dbforge();
+ if ( ! is_object($db) OR ! ($db instanceof CI_DB))
+ {
+ class_exists('CI_DB', FALSE) OR $this->database();
+ $db =& $CI->db;
+ }
require_once(BASEPATH.'database/DB_utility.php');
- require_once(BASEPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_utility.php');
- $class = 'CI_DB_'.$CI->db->dbdriver.'_utility';
+ require_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_utility.php');
+ $class = 'CI_DB_'.$db->dbdriver.'_utility';
- $CI->dbutil = new $class();
+ if ($return === TRUE)
+ {
+ return new $class($db);
+ }
+
+ $CI->dbutil = new $class($db);
+ return $this;
}
// --------------------------------------------------------------------
@@ -379,41 +431,58 @@ class CI_Loader {
/**
* Load the Database Forge Class
*
- * @return string
+ * @param object $db Database object
+ * @param bool $return Whether to return the DB Forge class object or not
+ * @return object
*/
- public function dbforge()
+ public function dbforge($db = NULL, $return = FALSE)
{
- if ( ! class_exists('CI_DB'))
+ $CI =& get_instance();
+ if ( ! is_object($db) OR ! ($db instanceof CI_DB))
{
- $this->database();
+ class_exists('CI_DB', FALSE) OR $this->database();
+ $db =& $CI->db;
}
- $CI =& get_instance();
-
require_once(BASEPATH.'database/DB_forge.php');
- require_once(BASEPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_forge.php');
- $class = 'CI_DB_'.$CI->db->dbdriver.'_forge';
+ require_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_forge.php');
- $CI->dbforge = new $class();
+ if ( ! empty($db->subdriver))
+ {
+ $driver_path = BASEPATH.'database/drivers/'.$db->dbdriver.'/subdrivers/'.$db->dbdriver.'_'.$db->subdriver.'_forge.php';
+ if (file_exists($driver_path))
+ {
+ require_once($driver_path);
+ $class = 'CI_DB_'.$db->dbdriver.'_'.$db->subdriver.'_forge';
+ }
+ }
+ else
+ {
+ $class = 'CI_DB_'.$db->dbdriver.'_forge';
+ }
+
+ if ($return === TRUE)
+ {
+ return new $class($db);
+ }
+
+ $CI->dbforge = new $class($db);
+ return $this;
}
// --------------------------------------------------------------------
/**
- * Load View
+ * View Loader
*
- * This function is used to load a "view" file. It has three parameters:
+ * Loads "view" files.
*
- * 1. The name of the "view" file to be included.
- * 2. An associative array of data to be extracted for use in the view.
- * 3. TRUE/FALSE - whether to return the data or load it. In
- * some cases it's advantageous to be able to return data so that
- * a developer can process it in some way.
- *
- * @param string
- * @param array
- * @param bool
- * @return void
+ * @param string $view View name
+ * @param array $vars An associative array of data
+ * to be extracted for use in the view
+ * @param bool $return Whether to return the view output
+ * or leave it to the Output class
+ * @return object|string
*/
public function view($view, $vars = array(), $return = FALSE)
{
@@ -423,13 +492,11 @@ class CI_Loader {
// --------------------------------------------------------------------
/**
- * Load File
+ * Generic File Loader
*
- * This is a generic file loader
- *
- * @param string
- * @param bool
- * @return string
+ * @param string $path File path
+ * @param bool $return Whether to return the file output
+ * @return object|string
*/
public function file($path, $return = FALSE)
{
@@ -444,26 +511,45 @@ class CI_Loader {
* Once variables are set they become available within
* the controller class and its "view" files.
*
- * @param array
- * @param string
- * @return void
+ * @param array|object|string $vars
+ * An associative array or object containing values
+ * to be set, or a value's name if string
+ * @param string $val Value to set, only used if $vars is a string
+ * @return object
*/
- public function vars($vars = array(), $val = '')
+ public function vars($vars, $val = '')
{
- if ($val != '' AND is_string($vars))
+ if (is_string($vars))
{
$vars = array($vars => $val);
}
$vars = $this->_ci_object_to_array($vars);
- if (is_array($vars) AND count($vars) > 0)
+ if (is_array($vars) && count($vars) > 0)
{
foreach ($vars as $key => $val)
{
$this->_ci_cached_vars[$key] = $val;
}
}
+
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Clear Cached Variables
+ *
+ * Clears the cached variables.
+ *
+ * @return CI_Loader
+ */
+ public function clear_vars()
+ {
+ $this->_ci_cached_vars = array();
+ return $this;
}
// --------------------------------------------------------------------
@@ -473,8 +559,8 @@ class CI_Loader {
*
* Check if a variable is set and retrieve it.
*
- * @param array
- * @return void
+ * @param string $key Variable name
+ * @return mixed The variable or NULL if not found
*/
public function get_var($key)
{
@@ -484,12 +570,24 @@ class CI_Loader {
// --------------------------------------------------------------------
/**
- * Load Helper
+ * Get Variables
*
- * This function loads the specified helper file.
+ * Retrieves all loaded variables.
*
- * @param mixed
- * @return void
+ * @return array
+ */
+ public function get_vars()
+ {
+ return $this->_ci_cached_vars;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Helper Loader
+ *
+ * @param string|string[] $helpers Helper name(s)
+ * @return object
*/
public function helper($helpers = array())
{
@@ -500,27 +598,34 @@ class CI_Loader {
continue;
}
- $ext_helper = APPPATH.'helpers/'.config_item('subclass_prefix').$helper.'.php';
-
// Is this a helper extension request?
- if (file_exists($ext_helper))
+ $ext_helper = config_item('subclass_prefix').$helper;
+ $ext_loaded = FALSE;
+ foreach ($this->_ci_helper_paths as $path)
+ {
+ if (file_exists($path.'helpers/'.$ext_helper.'.php'))
+ {
+ include_once($path.'helpers/'.$ext_helper.'.php');
+ $ext_loaded = TRUE;
+ }
+ }
+
+ // If we have loaded extensions - check if the base one is here
+ if ($ext_loaded === TRUE)
{
$base_helper = BASEPATH.'helpers/'.$helper.'.php';
-
if ( ! file_exists($base_helper))
{
show_error('Unable to load the requested file: helpers/'.$helper.'.php');
}
- include_once($ext_helper);
include_once($base_helper);
-
$this->_ci_helpers[$helper] = TRUE;
- log_message('debug', 'Helper loaded: '.$helper);
+ log_message('info', 'Helper loaded: '.$helper);
continue;
}
- // Try to load the helper
+ // No extensions found ... try loading regular helpers and/or overrides
foreach ($this->_ci_helper_paths as $path)
{
if (file_exists($path.'helpers/'.$helper.'.php'))
@@ -528,7 +633,7 @@ class CI_Loader {
include_once($path.'helpers/'.$helper.'.php');
$this->_ci_helpers[$helper] = TRUE;
- log_message('debug', 'Helper loaded: '.$helper);
+ log_message('info', 'Helper loaded: '.$helper);
break;
}
}
@@ -539,6 +644,8 @@ class CI_Loader {
show_error('Unable to load the requested file: helpers/'.$helper.'.php');
}
}
+
+ return $this;
}
// --------------------------------------------------------------------
@@ -546,82 +653,96 @@ class CI_Loader {
/**
* Load Helpers
*
- * This is simply an alias to the above function in case the
- * user has written the plural form of this function.
+ * An alias for the helper() method in case the developer has
+ * written the plural form of it.
*
- * @param array
- * @return void
+ * @uses CI_Loader::helper()
+ * @param string|string[] $helpers Helper name(s)
+ * @return object
*/
public function helpers($helpers = array())
{
- $this->helper($helpers);
+ return $this->helper($helpers);
}
// --------------------------------------------------------------------
/**
- * Loads a language file
+ * Language Loader
*
- * @param array
- * @param string
- * @return void
+ * Loads language files.
+ *
+ * @param string|string[] $files List of language file names to load
+ * @param string Language name
+ * @return object
*/
- public function language($file = array(), $lang = '')
+ public function language($files, $lang = '')
{
- $CI =& get_instance();
-
- if ( ! is_array($file))
- {
- $file = array($file);
- }
-
- foreach ($file as $langfile)
- {
- $CI->lang->load($langfile, $lang);
- }
+ get_instance()->lang->load($files, $lang);
+ return $this;
}
// --------------------------------------------------------------------
/**
- * Loads a config file
+ * Config Loader
*
- * @param string
- * @param bool
- * @param bool
- * @return void
+ * Loads a config file (an alias for CI_Config::load()).
+ *
+ * @uses CI_Config::load()
+ * @param string $file Configuration file name
+ * @param bool $use_sections Whether configuration values should be loaded into their own section
+ * @param bool $fail_gracefully Whether to just return FALSE or display an error message
+ * @return bool TRUE if the file was loaded correctly or FALSE on failure
*/
- public function config($file = '', $use_sections = FALSE, $fail_gracefully = FALSE)
+ public function config($file, $use_sections = FALSE, $fail_gracefully = FALSE)
{
- $CI =& get_instance();
- $CI->config->load($file, $use_sections, $fail_gracefully);
+ return get_instance()->config->load($file, $use_sections, $fail_gracefully);
}
// --------------------------------------------------------------------
/**
- * Driver
+ * Driver Loader
*
- * Loads a driver library
+ * Loads a driver library.
*
- * @param string the name of the class
- * @param mixed the optional parameters
- * @param string an optional object name
- * @return void
+ * @param string|string[] $library Driver name(s)
+ * @param array $params Optional parameters to pass to the driver
+ * @param string $object_name An optional object name to assign to
+ *
+ * @return object|bool Object or FALSE on failure if $library is a string
+ * and $object_name is set. CI_Loader instance otherwise.
*/
- public function driver($library = '', $params = NULL, $object_name = NULL)
+ public function driver($library, $params = NULL, $object_name = NULL)
{
- if ( ! class_exists('CI_Driver_Library'))
+ if (is_array($library))
{
- // we aren't instantiating an object here, that'll be done by the Library itself
- require BASEPATH.'libraries/Driver.php';
- }
+ foreach ($library as $key => $value)
+ {
+ if (is_int($key))
+ {
+ $this->driver($value, $params);
+ }
+ else
+ {
+ $this->driver($key, $params, $value);
+ }
+ }
- if ($library == '')
+ return $this;
+ }
+ elseif (empty($library))
{
return FALSE;
}
+ if ( ! class_exists('CI_Driver_Library', FALSE))
+ {
+ // We aren't instantiating an object here, just making the base class available
+ require BASEPATH.'libraries/Driver.php';
+ }
+
// We can save the loader some time since Drivers will *always* be in a subfolder,
// and typically identically named to the library
if ( ! strpos($library, '/'))
@@ -637,13 +758,19 @@ class CI_Loader {
/**
* Add Package Path
*
- * Prepends a parent path to the library, model, helper, and config path arrays
+ * Prepends a parent path to the library, model, helper and config
+ * path arrays.
*
- * @param string
- * @param boolean
- * @return void
+ * @see CI_Loader::$_ci_library_paths
+ * @see CI_Loader::$_ci_model_paths
+ * @see CI_Loader::$_ci_helper_paths
+ * @see CI_Config::$_config_paths
+ *
+ * @param string $path Path to add
+ * @param bool $view_cascade (default: TRUE)
+ * @return object
*/
- public function add_package_path($path, $view_cascade=TRUE)
+ public function add_package_path($path, $view_cascade = TRUE)
{
$path = rtrim($path, '/').'/';
@@ -655,7 +782,9 @@ class CI_Loader {
// Add config file path
$config =& $this->_ci_get_component('config');
- array_unshift($config->_config_paths, $path);
+ $config->_config_paths[] = $path;
+
+ return $this;
}
// --------------------------------------------------------------------
@@ -663,14 +792,14 @@ class CI_Loader {
/**
* Get Package Paths
*
- * Return a list of all package paths, by default it will ignore BASEPATH.
+ * Return a list of all package paths.
*
- * @param string
- * @return void
+ * @param bool $include_base Whether to include BASEPATH (default: FALSE)
+ * @return array
*/
public function get_package_paths($include_base = FALSE)
{
- return $include_base === TRUE ? $this->_ci_library_paths : $this->_ci_model_paths;
+ return ($include_base === TRUE) ? $this->_ci_library_paths : $this->_ci_model_paths;
}
// --------------------------------------------------------------------
@@ -678,24 +807,24 @@ class CI_Loader {
/**
* Remove Package Path
*
- * Remove a path from the library, model, and helper path arrays if it exists
- * If no path is provided, the most recently added path is removed.
+ * Remove a path from the library, model, helper and/or config
+ * path arrays if it exists. If no path is provided, the most recently
+ * added path will be removed removed.
*
- * @param type
- * @param bool
- * @return type
+ * @param string $path Path to remove
+ * @return object
*/
- public function remove_package_path($path = '', $remove_config_path = TRUE)
+ public function remove_package_path($path = '')
{
$config =& $this->_ci_get_component('config');
- if ($path == '')
+ if ($path === '')
{
- $void = array_shift($this->_ci_library_paths);
- $void = array_shift($this->_ci_model_paths);
- $void = array_shift($this->_ci_helper_paths);
- $void = array_shift($this->_ci_view_paths);
- $void = array_shift($config->_config_paths);
+ array_shift($this->_ci_library_paths);
+ array_shift($this->_ci_model_paths);
+ array_shift($this->_ci_helper_paths);
+ array_shift($this->_ci_view_paths);
+ array_pop($config->_config_paths);
}
else
{
@@ -725,32 +854,37 @@ class CI_Loader {
$this->_ci_model_paths = array_unique(array_merge($this->_ci_model_paths, array(APPPATH)));
$this->_ci_view_paths = array_merge($this->_ci_view_paths, array(APPPATH.'views/' => TRUE));
$config->_config_paths = array_unique(array_merge($config->_config_paths, array(APPPATH)));
+
+ return $this;
}
// --------------------------------------------------------------------
/**
- * Loader
+ * Internal CI Data Loader
+ *
+ * Used to load views and files.
*
- * This function is used to load views and files.
* Variables are prefixed with _ci_ to avoid symbol collision with
- * variables made available to view files
+ * variables made available to view files.
*
- * @param array
- * @return void
+ * @used-by CI_Loader::view()
+ * @used-by CI_Loader::file()
+ * @param array $_ci_data Data to load
+ * @return object
*/
protected function _ci_load($_ci_data)
{
// Set the default data variables
foreach (array('_ci_view', '_ci_vars', '_ci_path', '_ci_return') as $_ci_val)
{
- $$_ci_val = ( ! isset($_ci_data[$_ci_val])) ? FALSE : $_ci_data[$_ci_val];
+ $$_ci_val = isset($_ci_data[$_ci_val]) ? $_ci_data[$_ci_val] : FALSE;
}
$file_exists = FALSE;
// Set the path to the requested file
- if ($_ci_path != '')
+ if (is_string($_ci_path) && $_ci_path !== '')
{
$_ci_x = explode('/', $_ci_path);
$_ci_file = end($_ci_x);
@@ -758,13 +892,13 @@ class CI_Loader {
else
{
$_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);
- $_ci_file = ($_ci_ext == '') ? $_ci_view.'.php' : $_ci_view;
+ $_ci_file = ($_ci_ext === '') ? $_ci_view.'.php' : $_ci_view;
- foreach ($this->_ci_view_paths as $view_file => $cascade)
+ foreach ($this->_ci_view_paths as $_ci_view_file => $cascade)
{
- if (file_exists($view_file.$_ci_file))
+ if (file_exists($_ci_view_file.$_ci_file))
{
- $_ci_path = $view_file.$_ci_file;
+ $_ci_path = $_ci_view_file.$_ci_file;
$file_exists = TRUE;
break;
}
@@ -783,7 +917,6 @@ class CI_Loader {
// This allows anything loaded using $this->load (views, files, etc.)
// to become accessible from within the Controller and Model functions.
-
$_ci_CI =& get_instance();
foreach (get_object_vars($_ci_CI) as $_ci_key => $_ci_var)
{
@@ -796,13 +929,21 @@ class CI_Loader {
/*
* Extract and cache variables
*
- * You can either set variables using the dedicated $this->load_vars()
+ * You can either set variables using the dedicated $this->load->vars()
* function or via the second parameter of this function. We'll merge
* the two types and cache them so that views that are embedded within
* other views can have access to these variables.
*/
if (is_array($_ci_vars))
{
+ foreach (array_keys($_ci_vars) as $key)
+ {
+ if (strncmp($key, '_ci_', 4) === 0)
+ {
+ unset($_ci_vars[$key]);
+ }
+ }
+
$this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
}
extract($this->_ci_cached_vars);
@@ -812,29 +953,27 @@ class CI_Loader {
*
* We buffer the output for two reasons:
* 1. Speed. You get a significant speed boost.
- * 2. So that the final rendered template can be
- * post-processed by the output class. Why do we
- * need post processing? For one thing, in order to
- * show the elapsed page load time. Unless we
- * can intercept the content right before it's sent to
- * the browser and then stop the timer it won't be accurate.
+ * 2. So that the final rendered template can be post-processed by
+ * the output class. Why do we need post processing? For one thing,
+ * in order to show the elapsed page load time. Unless we can
+ * intercept the content right before it's sent to the browser and
+ * then stop the timer it won't be accurate.
*/
ob_start();
// If the PHP installation does not support short tags we'll
// do a little string replacement, changing the short tags
// to standard PHP echo statements.
-
- if ((bool) @ini_get('short_open_tag') === FALSE AND config_item('rewrite_short_tags') == TRUE)
+ if ( ! is_php('5.4') && ! ini_get('short_open_tag') && config_item('rewrite_short_tags') === TRUE)
{
- echo eval('?>'.preg_replace("/;*\s*\?>/", "; ?>", str_replace('=', ''.preg_replace('/;*\s*\?>/', '; ?>', str_replace('=', ' $this->_ci_ob_level + 1)
{
@@ -863,21 +1001,24 @@ class CI_Loader {
$_ci_CI->output->append_output(ob_get_contents());
@ob_end_clean();
}
+
+ return $this;
}
// --------------------------------------------------------------------
/**
- * Load class
+ * Internal CI Library Loader
*
- * This function loads the requested class.
+ * @used-by CI_Loader::library()
+ * @uses CI_Loader::_ci_init_library()
*
- * @param string the item that is being loaded
- * @param mixed any additional parameters
- * @param string an optional object name
+ * @param string $class Class name to load
+ * @param mixed $params Optional parameters to pass to the class constructor
+ * @param string $object_name Optional object name to assign to
* @return void
*/
- protected function _ci_load_class($class, $params = NULL, $object_name = NULL)
+ protected function _ci_load_library($class, $params = NULL, $object_name = NULL)
{
// Get the class name, and while we're at it trim any slashes.
// The directory path can be included as part of the class name,
@@ -886,128 +1027,184 @@ class CI_Loader {
// Was the path included with the class name?
// We look for a slash to determine this
- $subdir = '';
if (($last_slash = strrpos($class, '/')) !== FALSE)
{
// Extract the path
- $subdir = substr($class, 0, $last_slash + 1);
+ $subdir = substr($class, 0, ++$last_slash);
// Get the filename from the path
- $class = substr($class, $last_slash + 1);
+ $class = substr($class, $last_slash);
+ }
+ else
+ {
+ $subdir = '';
}
- // We'll test for both lowercase and capitalized versions of the file name
- foreach (array(ucfirst($class), strtolower($class)) as $class)
+ $class = ucfirst($class);
+
+ // Is this a stock library? There are a few special conditions if so ...
+ if (file_exists(BASEPATH.'libraries/'.$subdir.$class.'.php'))
{
- $subclass = APPPATH.'libraries/'.$subdir.config_item('subclass_prefix').$class.'.php';
+ return $this->_ci_load_stock_library($class, $subdir, $params, $object_name);
+ }
- // Is this a class extension request?
- if (file_exists($subclass))
+ // Let's search for the requested library file and load it.
+ foreach ($this->_ci_library_paths as $path)
+ {
+ // BASEPATH has already been checked for
+ if ($path === BASEPATH)
{
- $baseclass = BASEPATH.'libraries/'.ucfirst($class).'.php';
-
- if ( ! file_exists($baseclass))
- {
- log_message('error', "Unable to load the requested class: ".$class);
- show_error("Unable to load the requested class: ".$class);
- }
-
- // Safety: Was the class already loaded by a previous call?
- if (in_array($subclass, $this->_ci_loaded_files))
- {
- // Before we deem this to be a duplicate request, let's see
- // if a custom object name is being supplied. If so, we'll
- // return a new instance of the object
- if ( ! is_null($object_name))
- {
- $CI =& get_instance();
- if ( ! isset($CI->$object_name))
- {
- return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name);
- }
- }
-
- $is_duplicate = TRUE;
- log_message('debug', $class." class already loaded. Second attempt ignored.");
- return;
- }
-
- include_once($baseclass);
- include_once($subclass);
- $this->_ci_loaded_files[] = $subclass;
-
- return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name);
+ continue;
}
- // Lets search for the requested library file and load it.
- $is_duplicate = FALSE;
- foreach ($this->_ci_library_paths as $path)
+ $filepath = $path.'libraries/'.$subdir.$class.'.php';
+
+ // Safety: Was the class already loaded by a previous call?
+ if (class_exists($class, FALSE))
{
- $filepath = $path.'libraries/'.$subdir.$class.'.php';
-
- // Does the file exist? No? Bummer...
- if ( ! file_exists($filepath))
+ // Before we deem this to be a duplicate request, let's see
+ // if a custom object name is being supplied. If so, we'll
+ // return a new instance of the object
+ if ($object_name !== NULL)
{
- continue;
- }
-
- // Safety: Was the class already loaded by a previous call?
- if (in_array($filepath, $this->_ci_loaded_files))
- {
- // Before we deem this to be a duplicate request, let's see
- // if a custom object name is being supplied. If so, we'll
- // return a new instance of the object
- if ( ! is_null($object_name))
+ $CI =& get_instance();
+ if ( ! isset($CI->$object_name))
{
- $CI =& get_instance();
- if ( ! isset($CI->$object_name))
- {
- return $this->_ci_init_class($class, '', $params, $object_name);
- }
+ return $this->_ci_init_library($class, '', $params, $object_name);
}
-
- $is_duplicate = TRUE;
- log_message('debug', $class." class already loaded. Second attempt ignored.");
- return;
}
- include_once($filepath);
- $this->_ci_loaded_files[] = $filepath;
- return $this->_ci_init_class($class, '', $params, $object_name);
+ log_message('debug', $class.' class already loaded. Second attempt ignored.');
+ return;
+ }
+ // Does the file exist? No? Bummer...
+ elseif ( ! file_exists($filepath))
+ {
+ continue;
}
- } // END FOREACH
+ include_once($filepath);
+ return $this->_ci_init_library($class, '', $params, $object_name);
+ }
- // One last attempt. Maybe the library is in a subdirectory, but it wasn't specified?
- if ($subdir == '')
+ // One last attempt. Maybe the library is in a subdirectory, but it wasn't specified?
+ if ($subdir === '')
{
- $path = strtolower($class).'/'.$class;
- return $this->_ci_load_class($path, $params);
+ return $this->_ci_load_library($class.'/'.$class, $params, $object_name);
}
// If we got this far we were unable to find the requested class.
- // We do not issue errors if the load call failed due to a duplicate request
- if ($is_duplicate == FALSE)
- {
- log_message('error', "Unable to load the requested class: ".$class);
- show_error("Unable to load the requested class: ".$class);
- }
+ log_message('error', 'Unable to load the requested class: '.$class);
+ show_error('Unable to load the requested class: '.$class);
}
// --------------------------------------------------------------------
/**
- * Instantiates a class
+ * Internal CI Stock Library Loader
*
- * @param string
- * @param string
- * @param bool
- * @param string an optional object name
- * @return null
+ * @used-by CI_Loader::_ci_load_library()
+ * @uses CI_Loader::_ci_init_library()
+ *
+ * @param string $library Library name to load
+ * @param string $file_path Path to the library filename, relative to libraries/
+ * @param mixed $params Optional parameters to pass to the class constructor
+ * @param string $object_name Optional object name to assign to
+ * @return void
*/
- protected function _ci_init_class($class, $prefix = '', $config = FALSE, $object_name = NULL)
+ protected function _ci_load_stock_library($library_name, $file_path, $params, $object_name)
{
- // Is there an associated config file for this class? Note: these should always be lowercase
+ $prefix = 'CI_';
+
+ if (class_exists($prefix.$library_name, FALSE))
+ {
+ if (class_exists(config_item('subclass_prefix').$library_name, FALSE))
+ {
+ $prefix = config_item('subclass_prefix');
+ }
+
+ // Before we deem this to be a duplicate request, let's see
+ // if a custom object name is being supplied. If so, we'll
+ // return a new instance of the object
+ if ($object_name !== NULL)
+ {
+ $CI =& get_instance();
+ if ( ! isset($CI->$object_name))
+ {
+ return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
+ }
+ }
+
+ log_message('debug', $library_name.' class already loaded. Second attempt ignored.');
+ return;
+ }
+
+ $paths = $this->_ci_library_paths;
+ array_pop($paths); // BASEPATH
+ array_pop($paths); // APPPATH (needs to be the first path checked)
+ array_unshift($paths, APPPATH);
+
+ foreach ($paths as $path)
+ {
+ if (file_exists($path = $path.'libraries/'.$file_path.$library_name.'.php'))
+ {
+ // Override
+ include_once($path);
+ if (class_exists($prefix.$library_name, FALSE))
+ {
+ return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
+ }
+ else
+ {
+ log_message('debug', $path.' exists, but does not declare '.$prefix.$library_name);
+ }
+ }
+ }
+
+ include_once(BASEPATH.'libraries/'.$file_path.$library_name.'.php');
+
+ // Check for extensions
+ $subclass = config_item('subclass_prefix').$library_name;
+ foreach ($paths as $path)
+ {
+ if (file_exists($path = $path.'libraries/'.$file_path.$subclass.'.php'))
+ {
+ include_once($path);
+ if (class_exists($subclass, FALSE))
+ {
+ $prefix = config_item('subclass_prefix');
+ break;
+ }
+ else
+ {
+ log_message('debug', $path.' exists, but does not declare '.$subclass);
+ }
+ }
+ }
+
+ return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Internal CI Library Instantiator
+ *
+ * @used-by CI_Loader::_ci_load_stock_library()
+ * @used-by CI_Loader::_ci_load_library()
+ *
+ * @param string $class Class name
+ * @param string $prefix Class name prefix
+ * @param array|null|bool $config Optional configuration to pass to the class constructor:
+ * FALSE to skip;
+ * NULL to search in config paths;
+ * array containing configuration data
+ * @param string $object_name Optional object name to assign to
+ * @return void
+ */
+ protected function _ci_init_library($class, $prefix, $config = FALSE, $object_name = NULL)
+ {
+ // Is there an associated config file for this class? Note: these should always be lowercase
if ($config === NULL)
{
// Fetch the config paths containing any package paths
@@ -1015,117 +1212,111 @@ class CI_Loader {
if (is_array($config_component->_config_paths))
{
- // Break on the first found file, thus package files
- // are not overridden by default paths
+ $found = FALSE;
foreach ($config_component->_config_paths as $path)
{
// We test for both uppercase and lowercase, for servers that
- // are case-sensitive with regard to file names. Check for environment
- // first, global next
- if (defined('ENVIRONMENT') AND file_exists($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'))
+ // are case-sensitive with regard to file names. Load global first,
+ // override with environment next
+ if (file_exists($path.'config/'.strtolower($class).'.php'))
{
- include($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php');
- break;
+ include($path.'config/'.strtolower($class).'.php');
+ $found = TRUE;
}
- elseif (defined('ENVIRONMENT') AND file_exists($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'))
+ elseif (file_exists($path.'config/'.ucfirst(strtolower($class)).'.php'))
{
- include($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php');
- break;
+ include($path.'config/'.ucfirst(strtolower($class)).'.php');
+ $found = TRUE;
}
- elseif (file_exists($path .'config/'.strtolower($class).'.php'))
+
+ if (file_exists($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'))
{
- include($path .'config/'.strtolower($class).'.php');
- break;
+ include($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php');
+ $found = TRUE;
}
- elseif (file_exists($path .'config/'.ucfirst(strtolower($class)).'.php'))
+ elseif (file_exists($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'))
+ {
+ include($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php');
+ $found = TRUE;
+ }
+
+ // Break on the first found configuration, thus package
+ // files are not overridden by default paths
+ if ($found === TRUE)
{
- include($path .'config/'.ucfirst(strtolower($class)).'.php');
break;
}
}
}
}
- if ($prefix == '')
- {
- if (class_exists('CI_'.$class))
- {
- $name = 'CI_'.$class;
- }
- elseif (class_exists(config_item('subclass_prefix').$class))
- {
- $name = config_item('subclass_prefix').$class;
- }
- else
- {
- $name = $class;
- }
- }
- else
- {
- $name = $prefix.$class;
- }
+ $class_name = $prefix.$class;
// Is the class name valid?
- if ( ! class_exists($name))
+ if ( ! class_exists($class_name, FALSE))
{
- log_message('error', "Non-existent class: ".$name);
- show_error("Non-existent class: ".$class);
+ log_message('error', 'Non-existent class: '.$class_name);
+ show_error('Non-existent class: '.$class_name);
}
// Set the variable name we will assign the class to
- // Was a custom class name supplied? If so we'll use it
- $class = strtolower($class);
-
- if (is_null($object_name))
+ // Was a custom class name supplied? If so we'll use it
+ if (empty($object_name))
{
- $classvar = ( ! isset($this->_ci_varmap[$class])) ? $class : $this->_ci_varmap[$class];
+ $object_name = strtolower($class);
+ if (isset($this->_ci_varmap[$object_name]))
+ {
+ $object_name = $this->_ci_varmap[$object_name];
+ }
}
- else
+
+ // Don't overwrite existing properties
+ $CI =& get_instance();
+ if (isset($CI->$object_name))
{
- $classvar = $object_name;
+ if ($CI->$object_name instanceof $class_name)
+ {
+ log_message('debug', $class_name." has already been instantiated as '".$object_name."'. Second attempt aborted.");
+ return;
+ }
+
+ show_error("Resource '".$object_name."' already exists and is not a ".$class_name." instance.");
}
// Save the class name and object name
- $this->_ci_classes[$class] = $classvar;
+ $this->_ci_classes[$object_name] = $class;
// Instantiate the class
- $CI =& get_instance();
- if ($config !== NULL)
- {
- $CI->$classvar = new $name($config);
- }
- else
- {
- $CI->$classvar = new $name;
- }
+ $CI->$object_name = isset($config)
+ ? new $class_name($config)
+ : new $class_name();
}
// --------------------------------------------------------------------
/**
- * Autoloader
+ * CI Autoloader
*
- * The config/autoload.php file contains an array that permits sub-systems,
- * libraries, and helpers to be loaded automatically.
+ * Loads component listed in the config/autoload.php file.
*
- * @param array
+ * @used-by CI_Loader::initialize()
* @return void
*/
- private function _ci_autoloader()
+ protected function _ci_autoloader()
{
- if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/autoload.php'))
- {
- include(APPPATH.'config/'.ENVIRONMENT.'/autoload.php');
- }
- else
+ if (file_exists(APPPATH.'config/autoload.php'))
{
include(APPPATH.'config/autoload.php');
}
+ if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/autoload.php'))
+ {
+ include(APPPATH.'config/'.ENVIRONMENT.'/autoload.php');
+ }
+
if ( ! isset($autoload))
{
- return FALSE;
+ return;
}
// Autoload packages
@@ -1140,31 +1331,29 @@ class CI_Loader {
// Load any custom config file
if (count($autoload['config']) > 0)
{
- $CI =& get_instance();
- foreach ($autoload['config'] as $key => $val)
+ foreach ($autoload['config'] as $val)
{
- $CI->config->load($val);
+ $this->config($val);
}
}
// Autoload helpers and languages
foreach (array('helper', 'language') as $type)
{
- if (isset($autoload[$type]) AND count($autoload[$type]) > 0)
+ if (isset($autoload[$type]) && count($autoload[$type]) > 0)
{
$this->$type($autoload[$type]);
}
}
- // A little tweak to remain backward compatible
- // The $autoload['core'] item was deprecated
- if ( ! isset($autoload['libraries']) AND isset($autoload['core']))
+ // Autoload drivers
+ if (isset($autoload['drivers']))
{
- $autoload['libraries'] = $autoload['core'];
+ $this->driver($autoload['drivers']);
}
// Load libraries
- if (isset($autoload['libraries']) AND count($autoload['libraries']) > 0)
+ if (isset($autoload['libraries']) && count($autoload['libraries']) > 0)
{
// Load the database driver.
if (in_array('database', $autoload['libraries']))
@@ -1174,10 +1363,7 @@ class CI_Loader {
}
// Load all other libraries
- foreach ($autoload['libraries'] as $item)
- {
- $this->library($item);
- }
+ $this->library($autoload['libraries']);
}
// Autoload models
@@ -1190,24 +1376,27 @@ class CI_Loader {
// --------------------------------------------------------------------
/**
- * Object to Array
+ * CI Object to Array translator
*
- * Takes an object as input and converts the class variables to array key/vals
+ * Takes an object as input and converts the class variables to
+ * an associative array with key/value pairs.
*
- * @param object
+ * @param object $object Object data to translate
* @return array
*/
protected function _ci_object_to_array($object)
{
- return (is_object($object)) ? get_object_vars($object) : $object;
+ return is_object($object) ? get_object_vars($object) : $object;
}
// --------------------------------------------------------------------
/**
- * Get a reference to a specific library or model
+ * CI Component getter
*
- * @param string
+ * Get a reference to a specific library or model.
+ *
+ * @param string $component Component name
* @return bool
*/
protected function &_ci_get_component($component)
@@ -1221,29 +1410,28 @@ class CI_Loader {
/**
* Prep filename
*
- * This function preps the name of various items to make loading them more reliable.
+ * This function prepares filenames of various items to
+ * make their loading more reliable.
*
- * @param mixed
- * @param string
+ * @param string|string[] $filename Filename(s)
+ * @param string $extension Filename extension
* @return array
*/
protected function _ci_prep_filename($filename, $extension)
{
if ( ! is_array($filename))
{
- return array(strtolower(str_replace('.php', '', str_replace($extension, '', $filename)).$extension));
+ return array(strtolower(str_replace(array($extension, '.php'), '', $filename).$extension));
}
else
{
foreach ($filename as $key => $val)
{
- $filename[$key] = strtolower(str_replace('.php', '', str_replace($extension, '', $val)).$extension);
+ $filename[$key] = strtolower(str_replace(array($extension, '.php'), '', $val).$extension);
}
return $filename;
}
}
-}
-/* End of file Loader.php */
-/* Location: ./system/core/Loader.php */
\ No newline at end of file
+}
diff --git a/src/system/core/Log.php b/src/system/core/Log.php
new file mode 100644
index 00000000..1abdaa00
--- /dev/null
+++ b/src/system/core/Log.php
@@ -0,0 +1,247 @@
+ 1, 'DEBUG' => 2, 'INFO' => 3, 'ALL' => 4);
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ $config =& get_config();
+
+ $this->_log_path = ($config['log_path'] !== '') ? $config['log_path'] : APPPATH.'logs/';
+ $this->_file_ext = (isset($config['log_file_extension']) && $config['log_file_extension'] !== '')
+ ? ltrim($config['log_file_extension'], '.') : 'php';
+
+ file_exists($this->_log_path) OR mkdir($this->_log_path, 0755, TRUE);
+
+ if ( ! is_dir($this->_log_path) OR ! is_really_writable($this->_log_path))
+ {
+ $this->_enabled = FALSE;
+ }
+
+ if (is_numeric($config['log_threshold']))
+ {
+ $this->_threshold = (int) $config['log_threshold'];
+ }
+ elseif (is_array($config['log_threshold']))
+ {
+ $this->_threshold = 0;
+ $this->_threshold_array = array_flip($config['log_threshold']);
+ }
+
+ if ( ! empty($config['log_date_format']))
+ {
+ $this->_date_fmt = $config['log_date_format'];
+ }
+
+ if ( ! empty($config['log_file_permissions']) && is_int($config['log_file_permissions']))
+ {
+ $this->_file_permissions = $config['log_file_permissions'];
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Write Log File
+ *
+ * Generally this function will be called using the global log_message() function
+ *
+ * @param string $level The error level: 'error', 'debug' or 'info'
+ * @param string $msg The error message
+ * @return bool
+ */
+ public function write_log($level, $msg)
+ {
+ if ($this->_enabled === FALSE)
+ {
+ return FALSE;
+ }
+
+ $level = strtoupper($level);
+
+ if (( ! isset($this->_levels[$level]) OR ($this->_levels[$level] > $this->_threshold))
+ && ! isset($this->_threshold_array[$this->_levels[$level]]))
+ {
+ return FALSE;
+ }
+
+ $filepath = $this->_log_path.'log-'.date('Y-m-d').'.'.$this->_file_ext;
+ $message = '';
+
+ if ( ! file_exists($filepath))
+ {
+ $newfile = TRUE;
+ // Only add protection to php files
+ if ($this->_file_ext === 'php')
+ {
+ $message .= "\n\n";
+ }
+ }
+
+ if ( ! $fp = @fopen($filepath, 'ab'))
+ {
+ return FALSE;
+ }
+
+ flock($fp, LOCK_EX);
+
+ // Instantiating DateTime with microseconds appended to initial date is needed for proper support of this format
+ if (strpos($this->_date_fmt, 'u') !== FALSE)
+ {
+ $microtime_full = microtime(TRUE);
+ $microtime_short = sprintf("%06d", ($microtime_full - floor($microtime_full)) * 1000000);
+ $date = new DateTime(date('Y-m-d H:i:s.'.$microtime_short, $microtime_full));
+ $date = $date->format($this->_date_fmt);
+ }
+ else
+ {
+ $date = date($this->_date_fmt);
+ }
+
+ $message .= $this->_format_line($level, $date, $msg);
+
+ for ($written = 0, $length = strlen($message); $written < $length; $written += $result)
+ {
+ if (($result = fwrite($fp, substr($message, $written))) === FALSE)
+ {
+ break;
+ }
+ }
+
+ flock($fp, LOCK_UN);
+ fclose($fp);
+
+ if (isset($newfile) && $newfile === TRUE)
+ {
+ chmod($filepath, $this->_file_permissions);
+ }
+
+ return is_int($result);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Format the log line.
+ *
+ * This is for extensibility of log formatting
+ * If you want to change the log format, extend the CI_Log class and override this method
+ *
+ * @param string $level The error level
+ * @param string $date Formatted date string
+ * @param string $msg The log message
+ * @return string Formatted log line with a new line character '\n' at the end
+ */
+ protected function _format_line($level, $date, $message)
+ {
+ return $level.' - '.$date.' --> '.$message."\n";
+ }
+}
diff --git a/src/system/core/Model.php b/src/system/core/Model.php
index 9bbb1fcd..941881a9 100644
--- a/src/system/core/Model.php
+++ b/src/system/core/Model.php
@@ -1,58 +1,80 @@
-$key;
+ // Debugging note:
+ // If you're here because you're getting an error message
+ // saying 'Undefined Property: system/core/Model.php', it's
+ // most likely a typo in your model code.
+ return get_instance()->$key;
}
-}
-// END Model Class
-/* End of file Model.php */
-/* Location: ./system/core/Model.php */
\ No newline at end of file
+}
diff --git a/src/system/core/Output.php b/src/system/core/Output.php
index fc88f6e5..ec9c21b9 100644
--- a/src/system/core/Output.php
+++ b/src/system/core/Output.php
@@ -1,113 +1,147 @@
-_zlib_oc = @ini_get('zlib.output_compression');
+ $this->_zlib_oc = (bool) ini_get('zlib.output_compression');
+ $this->_compress_output = (
+ $this->_zlib_oc === FALSE
+ && config_item('compress_output') === TRUE
+ && extension_loaded('zlib')
+ );
// Get mime types for later
- if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'))
- {
- include APPPATH.'config/'.ENVIRONMENT.'/mimes.php';
- }
- else
- {
- include APPPATH.'config/mimes.php';
- }
+ $this->mimes =& get_mimes();
-
- $this->mime_types = $mimes;
-
- log_message('debug', "Output Class Initialized");
+ log_message('info', 'Output Class Initialized');
}
// --------------------------------------------------------------------
@@ -115,12 +149,11 @@ class CI_Output {
/**
* Get Output
*
- * Returns the current output string
+ * Returns the current output string.
*
- * @access public
* @return string
*/
- function get_output()
+ public function get_output()
{
return $this->final_output;
}
@@ -130,16 +163,14 @@ class CI_Output {
/**
* Set Output
*
- * Sets the output string
+ * Sets the output string.
*
- * @access public
- * @param string
- * @return void
+ * @param string $output Output data
+ * @return CI_Output
*/
- function set_output($output)
+ public function set_output($output)
{
$this->final_output = $output;
-
return $this;
}
@@ -148,23 +179,14 @@ class CI_Output {
/**
* Append Output
*
- * Appends data onto the output string
+ * Appends data onto the output string.
*
- * @access public
- * @param string
- * @return void
+ * @param string $output Data to append
+ * @return CI_Output
*/
- function append_output($output)
+ public function append_output($output)
{
- if ($this->final_output == '')
- {
- $this->final_output = $output;
- }
- else
- {
- $this->final_output .= $output;
- }
-
+ $this->final_output .= $output;
return $this;
}
@@ -173,52 +195,49 @@ class CI_Output {
/**
* Set Header
*
- * Lets you set a server header which will be outputted with the final display.
+ * Lets you set a server header which will be sent with the final output.
*
- * Note: If a file is cached, headers will not be sent. We need to figure out
- * how to permit header data to be saved with the cache data...
+ * Note: If a file is cached, headers will not be sent.
+ * @todo We need to figure out how to permit headers to be cached.
*
- * @access public
- * @param string
- * @param bool
- * @return void
+ * @param string $header Header
+ * @param bool $replace Whether to replace the old header value, if already set
+ * @return CI_Output
*/
- function set_header($header, $replace = TRUE)
+ public function set_header($header, $replace = TRUE)
{
// If zlib.output_compression is enabled it will compress the output,
// but it will not modify the content-length header to compensate for
// the reduction, causing the browser to hang waiting for more data.
// We'll just skip content-length in those cases.
-
- if ($this->_zlib_oc && strncasecmp($header, 'content-length', 14) == 0)
+ if ($this->_zlib_oc && strncasecmp($header, 'content-length', 14) === 0)
{
- return;
+ return $this;
}
$this->headers[] = array($header, $replace);
-
return $this;
}
// --------------------------------------------------------------------
/**
- * Set Content Type Header
+ * Set Content-Type Header
*
- * @access public
- * @param string extension of the file we're outputting
- * @return void
+ * @param string $mime_type Extension of the file we're outputting
+ * @param string $charset Character set (default: NULL)
+ * @return CI_Output
*/
- function set_content_type($mime_type)
+ public function set_content_type($mime_type, $charset = NULL)
{
if (strpos($mime_type, '/') === FALSE)
{
$extension = ltrim($mime_type, '.');
// Is this extension supported?
- if (isset($this->mime_types[$extension]))
+ if (isset($this->mimes[$extension]))
{
- $mime_type =& $this->mime_types[$extension];
+ $mime_type =& $this->mimes[$extension];
if (is_array($mime_type))
{
@@ -227,28 +246,88 @@ class CI_Output {
}
}
- $header = 'Content-Type: '.$mime_type;
+ $this->mime_type = $mime_type;
+
+ if (empty($charset))
+ {
+ $charset = config_item('charset');
+ }
+
+ $header = 'Content-Type: '.$mime_type
+ .(empty($charset) ? '' : '; charset='.$charset);
$this->headers[] = array($header, TRUE);
-
return $this;
}
// --------------------------------------------------------------------
/**
- * Set HTTP Status Header
- * moved to Common procedural functions in 1.7.2
+ * Get Current Content-Type Header
*
- * @access public
- * @param int the status code
- * @param string
- * @return void
+ * @return string 'text/html', if not already set
*/
- function set_status_header($code = 200, $text = '')
+ public function get_content_type()
+ {
+ for ($i = 0, $c = count($this->headers); $i < $c; $i++)
+ {
+ if (sscanf($this->headers[$i][0], 'Content-Type: %[^;]', $content_type) === 1)
+ {
+ return $content_type;
+ }
+ }
+
+ return 'text/html';
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Get Header
+ *
+ * @param string $header_name
+ * @return string
+ */
+ public function get_header($header)
+ {
+ // Combine headers already sent with our batched headers
+ $headers = array_merge(
+ // We only need [x][0] from our multi-dimensional array
+ array_map('array_shift', $this->headers),
+ headers_list()
+ );
+
+ if (empty($headers) OR empty($header))
+ {
+ return NULL;
+ }
+
+ for ($i = 0, $c = count($headers); $i < $c; $i++)
+ {
+ if (strncasecmp($header, $headers[$i], $l = strlen($header)) === 0)
+ {
+ return trim(substr($headers[$i], $l+1));
+ }
+ }
+
+ return NULL;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Set HTTP Status Header
+ *
+ * As of version 1.7.2, this is an alias for common function
+ * set_status_header().
+ *
+ * @param int $code Status code (default: 200)
+ * @param string $text Optional message
+ * @return CI_Output
+ */
+ public function set_status_header($code = 200, $text = '')
{
set_status_header($code, $text);
-
return $this;
}
@@ -257,14 +336,12 @@ class CI_Output {
/**
* Enable/disable Profiler
*
- * @access public
- * @param bool
- * @return void
+ * @param bool $val TRUE to enable or FALSE to disable
+ * @return CI_Output
*/
- function enable_profiler($val = TRUE)
+ public function enable_profiler($val = TRUE)
{
- $this->enable_profiler = (is_bool($val)) ? $val : TRUE;
-
+ $this->enable_profiler = is_bool($val) ? $val : TRUE;
return $this;
}
@@ -273,17 +350,23 @@ class CI_Output {
/**
* Set Profiler Sections
*
- * Allows override of default / config settings for Profiler section display
+ * Allows override of default/config settings for
+ * Profiler section display.
*
- * @access public
- * @param array
- * @return void
+ * @param array $sections Profiler sections
+ * @return CI_Output
*/
- function set_profiler_sections($sections)
+ public function set_profiler_sections($sections)
{
+ if (isset($sections['query_toggle_count']))
+ {
+ $this->_profiler_sections['query_toggle_count'] = (int) $sections['query_toggle_count'];
+ unset($sections['query_toggle_count']);
+ }
+
foreach ($sections as $section => $enable)
{
- $this->_profiler_sections[$section] = ($enable !== FALSE) ? TRUE : FALSE;
+ $this->_profiler_sections[$section] = ($enable !== FALSE);
}
return $this;
@@ -294,14 +377,12 @@ class CI_Output {
/**
* Set Cache
*
- * @access public
- * @param integer
- * @return void
+ * @param int $time Cache expiration time in minutes
+ * @return CI_Output
*/
- function cache($time)
+ public function cache($time)
{
- $this->cache_expiration = ( ! is_numeric($time)) ? 0 : $time;
-
+ $this->cache_expiration = is_numeric($time) ? $time : 0;
return $this;
}
@@ -310,27 +391,27 @@ class CI_Output {
/**
* Display Output
*
- * All "view" data is automatically put into this variable by the controller class:
+ * Processes and sends finalized output data to the browser along
+ * with any server headers and profile data. It also stops benchmark
+ * timers so the page rendering speed and memory usage can be shown.
*
- * $this->final_output
+ * Note: All "view" data is automatically put into $this->final_output
+ * by controller class.
*
- * This function sends the finalized output data to the browser along
- * with any server headers and profile data. It also stops the
- * benchmark timer so the page rendering speed and memory usage can be shown.
- *
- * @access public
- * @param string
- * @return mixed
+ * @uses CI_Output::$final_output
+ * @param string $output Output data override
+ * @return void
*/
- function _display($output = '')
+ public function _display($output = '')
{
- // Note: We use globals because we can't use $CI =& get_instance()
+ // Note: We use load_class() because we can't use $CI =& get_instance()
// since this function is sometimes called by the caching mechanism,
// which happens before the CI super object is available.
- global $BM, $CFG;
+ $BM =& load_class('Benchmark', 'core');
+ $CFG =& load_class('Config', 'core');
// Grab the super object if we can.
- if (class_exists('CI_Controller'))
+ if (class_exists('CI_Controller', FALSE))
{
$CI =& get_instance();
}
@@ -338,14 +419,14 @@ class CI_Output {
// --------------------------------------------------------------------
// Set the output data
- if ($output == '')
+ if ($output === '')
{
$output =& $this->final_output;
}
// --------------------------------------------------------------------
- // Do we need to write a cache file? Only if the controller does not have its
+ // Do we need to write a cache file? Only if the controller does not have its
// own _output() method and we are not dealing with a cache file, which we
// can determine by the existence of the $CI object above
if ($this->cache_expiration > 0 && isset($CI) && ! method_exists($CI, '_output'))
@@ -362,24 +443,18 @@ class CI_Output {
if ($this->parse_exec_vars === TRUE)
{
- $memory = ( ! function_exists('memory_get_usage')) ? '0' : round(memory_get_usage()/1024/1024, 2).'MB';
-
- $output = str_replace('{elapsed_time}', $elapsed, $output);
- $output = str_replace('{memory_usage}', $memory, $output);
+ $memory = round(memory_get_usage() / 1024 / 1024, 2).'MB';
+ $output = str_replace(array('{elapsed_time}', '{memory_usage}'), array($elapsed, $memory), $output);
}
// --------------------------------------------------------------------
// Is compression requested?
- if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc == FALSE)
+ if (isset($CI) // This means that we're not serving a cache file, if we were, it would already be compressed
+ && $this->_compress_output === TRUE
+ && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
{
- if (extension_loaded('zlib'))
- {
- if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) AND strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
- {
- ob_start('ob_gzhandler');
- }
- }
+ ob_start('ob_gzhandler');
}
// --------------------------------------------------------------------
@@ -400,20 +475,34 @@ class CI_Output {
// simply echo out the data and exit.
if ( ! isset($CI))
{
+ if ($this->_compress_output === TRUE)
+ {
+ if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
+ {
+ header('Content-Encoding: gzip');
+ header('Content-Length: '.strlen($output));
+ }
+ else
+ {
+ // User agent doesn't support gzip compression,
+ // so we'll have to decompress our cache
+ $output = gzinflate(substr($output, 10, -8));
+ }
+ }
+
echo $output;
- log_message('debug', "Final output sent to browser");
- log_message('debug', "Total execution time: ".$elapsed);
- return TRUE;
+ log_message('info', 'Final output sent to browser');
+ log_message('debug', 'Total execution time: '.$elapsed);
+ return;
}
// --------------------------------------------------------------------
// Do we need to generate profile data?
// If so, load the Profile class and run it.
- if ($this->enable_profiler == TRUE)
+ if ($this->enable_profiler === TRUE)
{
$CI->load->library('profiler');
-
if ( ! empty($this->_profiler_sections))
{
$CI->profiler->set_sections($this->_profiler_sections);
@@ -421,20 +510,13 @@ class CI_Output {
// If the output data contains closing