diff --git a/app/.htaccess b/app/.htaccess new file mode 100644 index 0000000..f24db0a --- /dev/null +++ b/app/.htaccess @@ -0,0 +1,6 @@ + + Require all denied + + + Deny from all + diff --git a/app/Config/App.php b/app/Config/App.php new file mode 100644 index 0000000..15b2000 --- /dev/null +++ b/app/Config/App.php @@ -0,0 +1,282 @@ + SYSPATH + * `]; + */ + $psr4 = [ + 'Config' => APPPATH . 'Config', + APP_NAMESPACE => APPPATH, // For custom namespace + 'App' => APPPATH, // To ensure filters, etc still found, + ]; + + /** + * ------------------------------------------------------------------- + * Class Map + * ------------------------------------------------------------------- + * The class map provides a map of class names and their exact + * location on the drive. Classes loaded in this manner will have + * slightly faster performance because they will not have to be + * searched for within one or more directories as they would if they + * were being autoloaded through a namespace. + * + * Prototype: + * + * $Config['classmap'] = [ + * 'MyClass' => '/path/to/class/file.php' + * ]; + */ + $classmap = []; + + //-------------------------------------------------------------------- + // Do Not Edit Below This Line + //-------------------------------------------------------------------- + + $this->psr4 = array_merge($this->psr4, $psr4); + $this->classmap = array_merge($this->classmap, $classmap); + + unset($psr4, $classmap); + } + + //-------------------------------------------------------------------- + +} diff --git a/app/Config/Boot/development.php b/app/Config/Boot/development.php new file mode 100644 index 0000000..61707a9 --- /dev/null +++ b/app/Config/Boot/development.php @@ -0,0 +1,32 @@ + '127.0.0.1', + 'port' => 11211, + 'weight' => 1, + 'raw' => false, + ]; + + /* + | ------------------------------------------------------------------------- + | Redis settings + | ------------------------------------------------------------------------- + | Your Redis server can be specified below, if you are using + | the Redis or Predis drivers. + | + */ + public $redis = [ + 'host' => '127.0.0.1', + 'password' => null, + 'port' => 6379, + 'timeout' => 0, + ]; + + /* + |-------------------------------------------------------------------------- + | Available Cache Handlers + |-------------------------------------------------------------------------- + | + | This is an array of cache engine alias' and class names. Only engines + | that are listed here are allowed to be used. + | + */ + public $validHandlers = [ + 'dummy' => \CodeIgniter\Cache\Handlers\DummyHandler::class, + 'file' => \CodeIgniter\Cache\Handlers\FileHandler::class, + 'memcached' => \CodeIgniter\Cache\Handlers\MemcachedHandler::class, + 'predis' => \CodeIgniter\Cache\Handlers\PredisHandler::class, + 'redis' => \CodeIgniter\Cache\Handlers\RedisHandler::class, + 'wincache' => \CodeIgniter\Cache\Handlers\WincacheHandler::class, + ]; +} diff --git a/app/Config/Constants.php b/app/Config/Constants.php new file mode 100644 index 0000000..3f7cef1 --- /dev/null +++ b/app/Config/Constants.php @@ -0,0 +1,77 @@ + '', + 'hostname' => 'localhost', + 'username' => '', + 'password' => '', + 'database' => '', + 'DBDriver' => 'MySQLi', + 'DBPrefix' => '', + 'pConnect' => false, + 'DBDebug' => (ENVIRONMENT !== 'production'), + 'cacheOn' => false, + 'cacheDir' => '', + 'charset' => 'utf8', + 'DBCollat' => 'utf8_general_ci', + 'swapPre' => '', + 'encrypt' => false, + 'compress' => false, + 'strictOn' => false, + 'failover' => [], + 'port' => 3306, + ]; + + /** + * This database connection is used when + * running PHPUnit database tests. + * + * @var array + */ + public $tests = [ + 'DSN' => '', + 'hostname' => '127.0.0.1', + 'username' => '', + 'password' => '', + 'database' => '', + 'DBDriver' => '', + 'DBPrefix' => 'db_', // Needed to ensure we're working correctly with prefixes live. DO NOT REMOVE. + 'pConnect' => false, + 'DBDebug' => (ENVIRONMENT !== 'production'), + 'cacheOn' => false, + 'cacheDir' => '', + 'charset' => 'utf8', + 'DBCollat' => 'utf8_general_ci', + 'swapPre' => '', + 'encrypt' => false, + 'compress' => false, + 'strictOn' => false, + 'failover' => [], + 'port' => 3306, + ]; + + //-------------------------------------------------------------------- + + public function __construct() + { + parent::__construct(); + + // Ensure that we always set the database group to 'tests' if + // we are currently running an automated test suite, so that + // we don't overwrite live data on accident. + if (ENVIRONMENT === 'testing') + { + $this->defaultGroup = 'tests'; + + // Under Travis-CI, we can set an ENV var named 'DB_GROUP' + // so that we can test against multiple databases. + if ($group = getenv('DB')) + { + if (is_file(TESTPATH . 'travis/Database.php')) + { + require TESTPATH . 'travis/Database.php'; + + if (! empty($dbconfig) && array_key_exists($group, $dbconfig)) + { + $this->tests = $dbconfig[$group]; + } + } + } + } + } + + //-------------------------------------------------------------------- + +} diff --git a/app/Config/DocTypes.php b/app/Config/DocTypes.php new file mode 100755 index 0000000..67d5dd2 --- /dev/null +++ b/app/Config/DocTypes.php @@ -0,0 +1,33 @@ + '', + 'xhtml1-strict' => '', + 'xhtml1-trans' => '', + 'xhtml1-frame' => '', + 'xhtml-basic11' => '', + 'html5' => '', + 'html4-strict' => '', + 'html4-trans' => '', + 'html4-frame' => '', + 'mathml1' => '', + 'mathml2' => '', + 'svg10' => '', + 'svg11' => '', + 'svg11-basic' => '', + 'svg11-tiny' => '', + 'xhtml-math-svg-xh' => '', + 'xhtml-math-svg-sh' => '', + 'xhtml-rdfa-1' => '', + 'xhtml-rdfa-2' => '', + ]; +} diff --git a/app/Config/Email.php b/app/Config/Email.php new file mode 100644 index 0000000..4566124 --- /dev/null +++ b/app/Config/Email.php @@ -0,0 +1,161 @@ +respond(); + }); +} diff --git a/app/Config/Exceptions.php b/app/Config/Exceptions.php new file mode 100644 index 0000000..c0245b2 --- /dev/null +++ b/app/Config/Exceptions.php @@ -0,0 +1,41 @@ + \App\Filters\CSRF::class, + 'toolbar' => \App\Filters\DebugToolbar::class, + 'honeypot' => \App\Filters\Honeypot::class, + ]; + + // Always applied before every request + public $globals = [ + 'before' => [ + //'honeypot' + // 'csrf', + ], + 'after' => [ + 'toolbar', + //'honeypot' + ], + ]; + + // Works on all of a particular HTTP method + // (GET, POST, etc) as BEFORE filters only + // like: 'post' => ['CSRF', 'throttle'], + public $methods = []; + + // List filter aliases and any before/after uri patterns + // that they should run on, like: + // 'isLoggedIn' => ['before' => ['account/*', 'profiles/*']], + public $filters = []; +} diff --git a/app/Config/ForeignCharacters.php b/app/Config/ForeignCharacters.php new file mode 100644 index 0000000..8ee6f11 --- /dev/null +++ b/app/Config/ForeignCharacters.php @@ -0,0 +1,6 @@ + \CodeIgniter\Format\JSONFormatter::class, + 'application/xml' => \CodeIgniter\Format\XMLFormatter::class, + 'text/xml' => \CodeIgniter\Format\XMLFormatter::class, + ]; + + //-------------------------------------------------------------------- + + /** + * A Factory method to return the appropriate formatter for the given mime type. + * + * @param string $mime + * + * @return \CodeIgniter\Format\FormatterInterface + */ + public function getFormatter(string $mime) + { + if (! array_key_exists($mime, $this->formatters)) + { + throw new \InvalidArgumentException('No Formatter defined for mime type: ' . $mime); + } + + $class = $this->formatters[$mime]; + + if (! class_exists($class)) + { + throw new \BadMethodCallException($class . ' is not a valid Formatter.'); + } + + return new $class(); + } + + //-------------------------------------------------------------------- + +} diff --git a/app/Config/Honeypot.php b/app/Config/Honeypot.php new file mode 100644 index 0000000..f4444a5 --- /dev/null +++ b/app/Config/Honeypot.php @@ -0,0 +1,34 @@ +{label}'; +} diff --git a/app/Config/Images.php b/app/Config/Images.php new file mode 100644 index 0000000..730ddee --- /dev/null +++ b/app/Config/Images.php @@ -0,0 +1,31 @@ + \CodeIgniter\Images\Handlers\GDHandler::class, + 'imagick' => \CodeIgniter\Images\Handlers\ImageMagickHandler::class, + ]; +} diff --git a/app/Config/Logger.php b/app/Config/Logger.php new file mode 100644 index 0000000..d32e195 --- /dev/null +++ b/app/Config/Logger.php @@ -0,0 +1,140 @@ + [ + + /* + * The log levels that this handler will handle. + */ + 'handles' => [ + 'critical', + 'alert', + 'emergency', + 'debug', + 'error', + 'info', + 'notice', + 'warning', + ], + + /* + * Leave this BLANK unless you would like to set something other than the default + * writeable/logs/ directory. Use a full getServer path with trailing slash. + */ + 'path' => WRITEPATH . 'logs/', + + /* + * The default filename extension for log files. The default 'php' allows for + * protecting the log files via basic scripting, when they are to be stored + * under a publicly accessible directory. + * + * Note: Leaving it blank will default to 'php'. + */ + 'fileExtension' => 'php', + + /* + * The file system permissions to be applied on newly created log files. + * + * IMPORTANT: This MUST be an integer (no quotes) and you MUST use octal + * integer notation (i.e. 0700, 0644, etc.) + */ + 'filePermissions' => 0644, + ], + + /** + * The ChromeLoggerHandler requires the use of the Chrome web browser + * and the ChromeLogger extension. Uncomment this block to use it. + */ + // 'CodeIgniter\Log\Handlers\ChromeLoggerHandler' => [ + // /* + // * The log levels that this handler will handle. + // */ + // 'handles' => ['critical', 'alert', 'emergency', 'debug', + // 'error', 'info', 'notice', 'warning'], + // ] + ]; +} diff --git a/app/Config/Migrations.php b/app/Config/Migrations.php new file mode 100644 index 0000000..c746a22 --- /dev/null +++ b/app/Config/Migrations.php @@ -0,0 +1,63 @@ +migration->current() this is the version that schema will + | be upgraded / downgraded to. + | + */ + public $currentVersion = 0; + +} diff --git a/app/Config/Mimes.php b/app/Config/Mimes.php new file mode 100644 index 0000000..a570ef5 --- /dev/null +++ b/app/Config/Mimes.php @@ -0,0 +1,534 @@ + [ + 'application/mac-binhex40', + 'application/mac-binhex', + 'application/x-binhex40', + 'application/x-mac-binhex40', + ], + 'cpt' => 'application/mac-compactpro', + 'csv' => [ + 'text/csv', + 'text/x-comma-separated-values', + 'text/comma-separated-values', + 'application/octet-stream', + 'application/vnd.ms-excel', + 'application/x-csv', + 'text/x-csv', + 'application/csv', + 'application/excel', + 'application/vnd.msexcel', + 'text/plain', + ], + 'bin' => [ + 'application/macbinary', + 'application/mac-binary', + 'application/octet-stream', + 'application/x-binary', + 'application/x-macbinary', + ], + 'dms' => 'application/octet-stream', + 'lha' => 'application/octet-stream', + 'lzh' => 'application/octet-stream', + 'exe' => [ + 'application/octet-stream', + 'application/x-msdownload', + ], + 'class' => 'application/octet-stream', + 'psd' => [ + 'application/x-photoshop', + 'image/vnd.adobe.photoshop', + ], + 'so' => 'application/octet-stream', + 'sea' => 'application/octet-stream', + 'dll' => 'application/octet-stream', + 'oda' => 'application/oda', + 'pdf' => [ + 'application/pdf', + 'application/force-download', + 'application/x-download', + 'binary/octet-stream', + ], + 'ai' => [ + 'application/pdf', + 'application/postscript', + ], + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'mif' => 'application/vnd.mif', + 'xls' => [ + 'application/vnd.ms-excel', + 'application/msexcel', + 'application/x-msexcel', + 'application/x-ms-excel', + 'application/x-excel', + 'application/x-dos_ms_excel', + 'application/xls', + 'application/x-xls', + 'application/excel', + 'application/download', + 'application/vnd.ms-office', + 'application/msword', + ], + 'ppt' => [ + 'application/vnd.ms-powerpoint', + 'application/powerpoint', + 'application/vnd.ms-office', + 'application/msword', + ], + 'pptx' => [ + 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'application/x-zip', + 'application/zip', + ], + 'wbxml' => 'application/wbxml', + 'wmlc' => 'application/wmlc', + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + 'dxr' => 'application/x-director', + 'dvi' => 'application/x-dvi', + 'gtar' => 'application/x-gtar', + 'gz' => 'application/x-gzip', + 'gzip' => 'application/x-gzip', + 'php' => [ + 'application/x-php', + 'application/x-httpd-php', + 'application/php', + 'text/php', + 'text/x-php', + 'application/x-httpd-php-source', + ], + 'php4' => 'application/x-httpd-php', + 'php3' => 'application/x-httpd-php', + 'phtml' => 'application/x-httpd-php', + 'phps' => 'application/x-httpd-php-source', + 'js' => [ + 'application/x-javascript', + 'text/plain', + ], + 'swf' => 'application/x-shockwave-flash', + 'sit' => 'application/x-stuffit', + 'tar' => 'application/x-tar', + 'tgz' => [ + 'application/x-tar', + 'application/x-gzip-compressed', + ], + 'z' => 'application/x-compress', + 'xhtml' => 'application/xhtml+xml', + 'xht' => 'application/xhtml+xml', + 'zip' => [ + 'application/x-zip', + 'application/zip', + 'application/x-zip-compressed', + 'application/s-compressed', + 'multipart/x-zip', + ], + 'rar' => [ + 'application/x-rar', + 'application/rar', + 'application/x-rar-compressed', + ], + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mpga' => 'audio/mpeg', + 'mp2' => 'audio/mpeg', + 'mp3' => [ + 'audio/mpeg', + 'audio/mpg', + 'audio/mpeg3', + 'audio/mp3', + ], + 'aif' => [ + 'audio/x-aiff', + 'audio/aiff', + ], + 'aiff' => [ + 'audio/x-aiff', + 'audio/aiff', + ], + 'aifc' => 'audio/x-aiff', + 'ram' => 'audio/x-pn-realaudio', + 'rm' => 'audio/x-pn-realaudio', + 'rpm' => 'audio/x-pn-realaudio-plugin', + 'ra' => 'audio/x-realaudio', + 'rv' => 'video/vnd.rn-realvideo', + 'wav' => [ + 'audio/x-wav', + 'audio/wave', + 'audio/wav', + ], + 'bmp' => [ + 'image/bmp', + 'image/x-bmp', + 'image/x-bitmap', + 'image/x-xbitmap', + 'image/x-win-bitmap', + 'image/x-windows-bmp', + 'image/ms-bmp', + 'image/x-ms-bmp', + 'application/bmp', + 'application/x-bmp', + 'application/x-win-bitmap', + ], + 'gif' => 'image/gif', + 'jpg' => [ + 'image/jpeg', + 'image/pjpeg', + ], + 'jpeg' => [ + 'image/jpeg', + 'image/pjpeg', + ], + 'jpe' => [ + 'image/jpeg', + 'image/pjpeg', + ], + 'jp2' => [ + 'image/jp2', + 'video/mj2', + 'image/jpx', + 'image/jpm', + ], + 'j2k' => [ + 'image/jp2', + 'video/mj2', + 'image/jpx', + 'image/jpm', + ], + 'jpf' => [ + 'image/jp2', + 'video/mj2', + 'image/jpx', + 'image/jpm', + ], + 'jpg2' => [ + 'image/jp2', + 'video/mj2', + 'image/jpx', + 'image/jpm', + ], + 'jpx' => [ + 'image/jp2', + 'video/mj2', + 'image/jpx', + 'image/jpm', + ], + 'jpm' => [ + 'image/jp2', + 'video/mj2', + 'image/jpx', + 'image/jpm', + ], + 'mj2' => [ + 'image/jp2', + 'video/mj2', + 'image/jpx', + 'image/jpm', + ], + 'mjp2' => [ + 'image/jp2', + 'video/mj2', + 'image/jpx', + 'image/jpm', + ], + 'png' => [ + 'image/png', + 'image/x-png', + ], + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'css' => [ + 'text/css', + 'text/plain', + ], + 'html' => [ + 'text/html', + 'text/plain', + ], + 'htm' => [ + 'text/html', + 'text/plain', + ], + 'shtml' => [ + 'text/html', + 'text/plain', + ], + 'txt' => 'text/plain', + 'text' => 'text/plain', + 'log' => [ + 'text/plain', + 'text/x-log', + ], + 'rtx' => 'text/richtext', + 'rtf' => 'text/rtf', + 'xml' => [ + 'application/xml', + 'text/xml', + 'text/plain', + ], + 'xsl' => [ + 'application/xml', + 'text/xsl', + 'text/xml', + ], + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'avi' => [ + 'video/x-msvideo', + 'video/msvideo', + 'video/avi', + 'application/x-troff-msvideo', + ], + 'movie' => 'video/x-sgi-movie', + 'doc' => [ + 'application/msword', + 'application/vnd.ms-office', + ], + 'docx' => [ + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/zip', + 'application/msword', + 'application/x-zip', + ], + 'dot' => [ + 'application/msword', + 'application/vnd.ms-office', + ], + 'dotx' => [ + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/zip', + 'application/msword', + ], + 'xlsx' => [ + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'application/zip', + 'application/vnd.ms-excel', + 'application/msword', + 'application/x-zip', + ], + 'word' => [ + 'application/msword', + 'application/octet-stream', + ], + 'xl' => 'application/excel', + 'eml' => 'message/rfc822', + 'json' => [ + 'application/json', + 'text/json', + ], + 'pem' => [ + 'application/x-x509-user-cert', + 'application/x-pem-file', + 'application/octet-stream', + ], + 'p10' => [ + 'application/x-pkcs10', + 'application/pkcs10', + ], + 'p12' => 'application/x-pkcs12', + 'p7a' => 'application/x-pkcs7-signature', + 'p7c' => [ + 'application/pkcs7-mime', + 'application/x-pkcs7-mime', + ], + 'p7m' => [ + 'application/pkcs7-mime', + 'application/x-pkcs7-mime', + ], + 'p7r' => 'application/x-pkcs7-certreqresp', + 'p7s' => 'application/pkcs7-signature', + 'crt' => [ + 'application/x-x509-ca-cert', + 'application/x-x509-user-cert', + 'application/pkix-cert', + ], + 'crl' => [ + 'application/pkix-crl', + 'application/pkcs-crl', + ], + 'der' => 'application/x-x509-ca-cert', + 'kdb' => 'application/octet-stream', + 'pgp' => 'application/pgp', + 'gpg' => 'application/gpg-keys', + 'sst' => 'application/octet-stream', + 'csr' => 'application/octet-stream', + 'rsa' => 'application/x-pkcs7', + 'cer' => [ + 'application/pkix-cert', + 'application/x-x509-ca-cert', + ], + '3g2' => 'video/3gpp2', + '3gp' => [ + 'video/3gp', + 'video/3gpp', + ], + 'mp4' => 'video/mp4', + 'm4a' => 'audio/x-m4a', + 'f4v' => [ + 'video/mp4', + 'video/x-f4v', + ], + 'flv' => 'video/x-flv', + 'webm' => 'video/webm', + 'aac' => 'audio/x-acc', + 'm4u' => 'application/vnd.mpegurl', + 'm3u' => 'text/plain', + 'xspf' => 'application/xspf+xml', + 'vlc' => 'application/videolan', + 'wmv' => [ + 'video/x-ms-wmv', + 'video/x-ms-asf', + ], + 'au' => 'audio/x-au', + 'ac3' => 'audio/ac3', + 'flac' => 'audio/x-flac', + 'ogg' => [ + 'audio/ogg', + 'video/ogg', + 'application/ogg', + ], + 'kmz' => [ + 'application/vnd.google-earth.kmz', + 'application/zip', + 'application/x-zip', + ], + 'kml' => [ + 'application/vnd.google-earth.kml+xml', + 'application/xml', + 'text/xml', + ], + 'ics' => 'text/calendar', + 'ical' => 'text/calendar', + 'zsh' => 'text/x-scriptzsh', + '7zip' => [ + 'application/x-compressed', + 'application/x-zip-compressed', + 'application/zip', + 'multipart/x-zip', + ], + 'cdr' => [ + 'application/cdr', + 'application/coreldraw', + 'application/x-cdr', + 'application/x-coreldraw', + 'image/cdr', + 'image/x-cdr', + 'zz-application/zz-winassoc-cdr', + ], + 'wma' => [ + 'audio/x-ms-wma', + 'video/x-ms-asf', + ], + 'jar' => [ + 'application/java-archive', + 'application/x-java-application', + 'application/x-jar', + 'application/x-compressed', + ], + 'svg' => [ + 'image/svg+xml', + 'application/xml', + 'text/xml', + ], + 'vcf' => 'text/x-vcard', + 'srt' => [ + 'text/srt', + 'text/plain', + ], + 'vtt' => [ + 'text/vtt', + 'text/plain', + ], + 'ico' => [ + 'image/x-icon', + 'image/x-ico', + 'image/vnd.microsoft.icon', + ], + ]; + + //-------------------------------------------------------------------- + + /** + * Attempts to determine the best mime type for the given file extension. + * + * @param string $extension + * + * @return string|null The mime type found, or none if unable to determine. + */ + public static function guessTypeFromExtension(string $extension) + { + $extension = trim(strtolower($extension), '. '); + + if (! array_key_exists($extension, static::$mimes)) + { + return null; + } + + return is_array(static::$mimes[$extension]) ? static::$mimes[$extension][0] : static::$mimes[$extension]; + } + + //-------------------------------------------------------------------- + + /** + * Attempts to determine the best file extension for a given mime type. + * + * @param string $type + * @param string $proposed_extension - default extension (in case there is more than one with the same mime type) + * + * @return string|null The extension determined, or null if unable to match. + */ + public static function guessExtensionFromType(string $type, ?string $proposed_extension = null) + { + $type = trim(strtolower($type), '. '); + + $proposed_extension = trim(strtolower($proposed_extension)); + + if (! is_null($proposed_extension) && array_key_exists($proposed_extension, static::$mimes) && in_array($type, is_string(static::$mimes[$proposed_extension]) ? [static::$mimes[$proposed_extension]] : static::$mimes[$proposed_extension])) + { + return $proposed_extension; + } + + foreach (static::$mimes as $ext => $types) + { + if (is_string($types) && $types === $type) + { + return $ext; + } + else if (is_array($types) && in_array($type, $types)) + { + return $ext; + } + } + + return null; + } + + //-------------------------------------------------------------------- + +} diff --git a/app/Config/Modules.php b/app/Config/Modules.php new file mode 100644 index 0000000..2dcd22b --- /dev/null +++ b/app/Config/Modules.php @@ -0,0 +1,57 @@ +enabled) + { + return false; + } + + $alias = strtolower($alias); + + return in_array($alias, $this->activeExplorers); + } +} diff --git a/app/Config/Pager.php b/app/Config/Pager.php new file mode 100644 index 0000000..9b6ed83 --- /dev/null +++ b/app/Config/Pager.php @@ -0,0 +1,35 @@ + 'CodeIgniter\Pager\Views\default_full', + 'default_simple' => 'CodeIgniter\Pager\Views\default_simple', + 'default_head' => 'CodeIgniter\Pager\Views\default_head', + ]; + + /* + |-------------------------------------------------------------------------- + | Items Per Page + |-------------------------------------------------------------------------- + | + | The default number of results shown in a single page. + | + */ + public $perPage = 20; +} diff --git a/app/Config/Paths.php b/app/Config/Paths.php new file mode 100644 index 0000000..5ddbdad --- /dev/null +++ b/app/Config/Paths.php @@ -0,0 +1,77 @@ +defaultNamespace() + * + * Modifies the namespace that is added to a controller if it doesn't + * already have one. By default this is the global namespace (\). + * + * $routes->defaultController() + * + * Changes the name of the class used as a controller when the route + * points to a folder instead of a class. + * + * $routes->defaultMethod() + * + * Assigns the method inside the controller that is ran when the + * Router is unable to determine the appropriate method to run. + * + * $routes->setAutoRoute() + * + * Determines whether the Router will attempt to match URIs to + * Controllers when no specific route has been defined. If false, + * only routes that have been defined here will be available. + */ +$routes->setDefaultNamespace('App\Controllers'); +$routes->setDefaultController('Home'); +$routes->setDefaultMethod('index'); +$routes->setTranslateURIDashes(false); +$routes->set404Override(); +$routes->setAutoRoute(true); + +/** + * -------------------------------------------------------------------- + * Route Definitions + * -------------------------------------------------------------------- + */ + +// We get a performance increase by specifying the default +// route since we don't have to scan directories. +$routes->get('/', 'Home::index'); + +/** + * -------------------------------------------------------------------- + * Additional Routing + * -------------------------------------------------------------------- + * + * There will often be times that you need additional routing and you + * need to it be able to override any defaults in this file. Environment + * based routes is one such time. require() additional route files here + * to make that happen. + * + * You will have access to the $routes object within that file without + * needing to reload it. + */ +if (file_exists(APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php')) +{ + require APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php'; +} diff --git a/app/Config/Services.php b/app/Config/Services.php new file mode 100644 index 0000000..3eab915 --- /dev/null +++ b/app/Config/Services.php @@ -0,0 +1,49 @@ + 'Windows 10', + 'windows nt 6.3' => 'Windows 8.1', + 'windows nt 6.2' => 'Windows 8', + 'windows nt 6.1' => 'Windows 7', + 'windows nt 6.0' => 'Windows Vista', + 'windows nt 5.2' => 'Windows 2003', + 'windows nt 5.1' => 'Windows XP', + 'windows nt 5.0' => 'Windows 2000', + 'windows nt 4.0' => 'Windows NT 4.0', + 'winnt4.0' => 'Windows NT 4.0', + 'winnt 4.0' => 'Windows NT', + 'winnt' => 'Windows NT', + 'windows 98' => 'Windows 98', + 'win98' => 'Windows 98', + 'windows 95' => 'Windows 95', + 'win95' => 'Windows 95', + 'windows phone' => 'Windows Phone', + 'windows' => 'Unknown Windows OS', + 'android' => 'Android', + 'blackberry' => 'BlackBerry', + 'iphone' => 'iOS', + 'ipad' => 'iOS', + 'ipod' => 'iOS', + 'os x' => 'Mac OS X', + 'ppc mac' => 'Power PC Mac', + 'freebsd' => 'FreeBSD', + 'ppc' => 'Macintosh', + 'linux' => 'Linux', + 'debian' => 'Debian', + 'sunos' => 'Sun Solaris', + 'beos' => 'BeOS', + 'apachebench' => 'ApacheBench', + 'aix' => 'AIX', + 'irix' => 'Irix', + 'osf' => 'DEC OSF', + 'hp-ux' => 'HP-UX', + 'netbsd' => 'NetBSD', + 'bsdi' => 'BSDi', + 'openbsd' => 'OpenBSD', + 'gnu' => 'GNU/Linux', + 'unix' => 'Unknown Unix OS', + 'symbian' => 'Symbian OS', + ]; + + // The order of this array should NOT be changed. Many browsers return + // multiple browser types so we want to identify the sub-type first. + public $browsers = [ + 'OPR' => 'Opera', + 'Flock' => 'Flock', + 'Edge' => 'Spartan', + 'Chrome' => 'Chrome', + // Opera 10+ always reports Opera/9.80 and appends Version/ to the user agent string + 'Opera.*?Version' => 'Opera', + 'Opera' => 'Opera', + 'MSIE' => 'Internet Explorer', + 'Internet Explorer' => 'Internet Explorer', + 'Trident.* rv' => 'Internet Explorer', + 'Shiira' => 'Shiira', + 'Firefox' => 'Firefox', + 'Chimera' => 'Chimera', + 'Phoenix' => 'Phoenix', + 'Firebird' => 'Firebird', + 'Camino' => 'Camino', + 'Netscape' => 'Netscape', + 'OmniWeb' => 'OmniWeb', + 'Safari' => 'Safari', + 'Mozilla' => 'Mozilla', + 'Konqueror' => 'Konqueror', + 'icab' => 'iCab', + 'Lynx' => 'Lynx', + 'Links' => 'Links', + 'hotjava' => 'HotJava', + 'amaya' => 'Amaya', + 'IBrowse' => 'IBrowse', + 'Maxthon' => 'Maxthon', + 'Ubuntu' => 'Ubuntu Web Browser', + 'Vivaldi' => 'Vivaldi', + ]; + + public $mobiles = [ + // legacy array, old values commented out + 'mobileexplorer' => 'Mobile Explorer', + // 'openwave' => 'Open Wave', + // 'opera mini' => 'Opera Mini', + // 'operamini' => 'Opera Mini', + // 'elaine' => 'Palm', + 'palmsource' => 'Palm', + // 'digital paths' => 'Palm', + // 'avantgo' => 'Avantgo', + // 'xiino' => 'Xiino', + 'palmscape' => 'Palmscape', + // 'nokia' => 'Nokia', + // 'ericsson' => 'Ericsson', + // 'blackberry' => 'BlackBerry', + // 'motorola' => 'Motorola' + + // Phones and Manufacturers + 'motorola' => 'Motorola', + 'nokia' => 'Nokia', + 'palm' => 'Palm', + 'iphone' => 'Apple iPhone', + 'ipad' => 'iPad', + 'ipod' => 'Apple iPod Touch', + 'sony' => 'Sony Ericsson', + 'ericsson' => 'Sony Ericsson', + 'blackberry' => 'BlackBerry', + 'cocoon' => 'O2 Cocoon', + 'blazer' => 'Treo', + 'lg' => 'LG', + 'amoi' => 'Amoi', + 'xda' => 'XDA', + 'mda' => 'MDA', + 'vario' => 'Vario', + 'htc' => 'HTC', + 'samsung' => 'Samsung', + 'sharp' => 'Sharp', + 'sie-' => 'Siemens', + 'alcatel' => 'Alcatel', + 'benq' => 'BenQ', + 'ipaq' => 'HP iPaq', + 'mot-' => 'Motorola', + 'playstation portable' => 'PlayStation Portable', + 'playstation 3' => 'PlayStation 3', + 'playstation vita' => 'PlayStation Vita', + 'hiptop' => 'Danger Hiptop', + 'nec-' => 'NEC', + 'panasonic' => 'Panasonic', + 'philips' => 'Philips', + 'sagem' => 'Sagem', + 'sanyo' => 'Sanyo', + 'spv' => 'SPV', + 'zte' => 'ZTE', + 'sendo' => 'Sendo', + 'nintendo dsi' => 'Nintendo DSi', + 'nintendo ds' => 'Nintendo DS', + 'nintendo 3ds' => 'Nintendo 3DS', + 'wii' => 'Nintendo Wii', + 'open web' => 'Open Web', + 'openweb' => 'OpenWeb', + + // Operating Systems + 'android' => 'Android', + 'symbian' => 'Symbian', + 'SymbianOS' => 'SymbianOS', + 'elaine' => 'Palm', + 'series60' => 'Symbian S60', + 'windows ce' => 'Windows CE', + + // Browsers + 'obigo' => 'Obigo', + 'netfront' => 'Netfront Browser', + 'openwave' => 'Openwave Browser', + 'mobilexplorer' => 'Mobile Explorer', + 'operamini' => 'Opera Mini', + 'opera mini' => 'Opera Mini', + 'opera mobi' => 'Opera Mobile', + 'fennec' => 'Firefox Mobile', + + // Other + 'digital paths' => 'Digital Paths', + 'avantgo' => 'AvantGo', + 'xiino' => 'Xiino', + 'novarra' => 'Novarra Transcoder', + 'vodafone' => 'Vodafone', + 'docomo' => 'NTT DoCoMo', + 'o2' => 'O2', + + // Fallback + 'mobile' => 'Generic Mobile', + 'wireless' => 'Generic Mobile', + 'j2me' => 'Generic Mobile', + 'midp' => 'Generic Mobile', + 'cldc' => 'Generic Mobile', + 'up.link' => 'Generic Mobile', + 'up.browser' => 'Generic Mobile', + 'smartphone' => 'Generic Mobile', + 'cellphone' => 'Generic Mobile', + ]; + + // There are hundreds of bots but these are the most common. + public $robots = [ + 'googlebot' => 'Googlebot', + 'msnbot' => 'MSNBot', + 'baiduspider' => 'Baiduspider', + 'bingbot' => 'Bing', + 'slurp' => 'Inktomi Slurp', + 'yahoo' => 'Yahoo', + 'ask jeeves' => 'Ask Jeeves', + 'fastcrawler' => 'FastCrawler', + 'infoseek' => 'InfoSeek Robot 1.0', + 'lycos' => 'Lycos', + 'yandex' => 'YandexBot', + 'mediapartners-google' => 'MediaPartners Google', + 'CRAZYWEBCRAWLER' => 'Crazy Webcrawler', + 'adsbot-google' => 'AdsBot Google', + 'feedfetcher-google' => 'Feedfetcher Google', + 'curious george' => 'Curious George', + 'ia_archiver' => 'Alexa Crawler', + 'MJ12bot' => 'Majestic-12', + 'Uptimebot' => 'Uptimebot', + ]; +} diff --git a/app/Config/Validation.php b/app/Config/Validation.php new file mode 100644 index 0000000..97f08c7 --- /dev/null +++ b/app/Config/Validation.php @@ -0,0 +1,36 @@ + 'CodeIgniter\Validation\Views\list', + 'single' => 'CodeIgniter\Validation\Views\single', + ]; + + //-------------------------------------------------------------------- + // Rules + //-------------------------------------------------------------------- +} diff --git a/app/Config/View.php b/app/Config/View.php new file mode 100644 index 0000000..f66b253 --- /dev/null +++ b/app/Config/View.php @@ -0,0 +1,34 @@ +isCLI()) + { + return; + } + + $security = Services::security(); + + try + { + $security->CSRFVerify($request); + } + catch (SecurityException $e) + { + if (config('App')->CSRFRedirect && ! $request->isAJAX()) + { + return redirect()->back()->with('error', $e->getMessage()); + } + + throw $e; + } + } + + //-------------------------------------------------------------------- + + /** + * We don't have anything to do here. + * + * @param RequestInterface|\CodeIgniter\HTTP\IncomingRequest $request + * @param ResponseInterface|\CodeIgniter\HTTP\Response $response + * + * @return mixed + */ + public function after(RequestInterface $request, ResponseInterface $response) + { + } + + //-------------------------------------------------------------------- +} diff --git a/app/Filters/DebugToolbar.php b/app/Filters/DebugToolbar.php new file mode 100644 index 0000000..8616bc3 --- /dev/null +++ b/app/Filters/DebugToolbar.php @@ -0,0 +1,38 @@ +prepare(); + } + + //-------------------------------------------------------------------- +} diff --git a/app/Filters/Honeypot.php b/app/Filters/Honeypot.php new file mode 100644 index 0000000..a16a55c --- /dev/null +++ b/app/Filters/Honeypot.php @@ -0,0 +1,44 @@ +hasContent($request)) + { + throw HoneypotException::isBot(); + } + } + + /** + * Attach a honypot to the current response. + * + * @param CodeIgniter\HTTP\RequestInterface $request + * @param CodeIgniter\HTTP\ResponseInterface $response + * @return mixed + */ + public function after(RequestInterface $request, ResponseInterface $response) + { + $honeypot = Services::honeypot(new \Config\Honeypot()); + $honeypot->attachHoneypot($response); + } + +} diff --git a/app/Filters/Throttle.php b/app/Filters/Throttle.php new file mode 100644 index 0000000..b2659e5 --- /dev/null +++ b/app/Filters/Throttle.php @@ -0,0 +1,46 @@ +check($request->getIPAddress(), 60, MINUTE) === false) + { + return Services::response()->setStatusCode(429); + } + } + + //-------------------------------------------------------------------- + + /** + * We don't have anything to do here. + * + * @param RequestInterface|\CodeIgniter\HTTP\IncomingRequest $request + * @param ResponseInterface|\CodeIgniter\HTTP\Response $response + * + * @return mixed + */ + public function after(RequestInterface $request, ResponseInterface $response) + { + } + + //-------------------------------------------------------------------- +} diff --git a/app/Helpers/.gitkeep b/app/Helpers/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/Helpers/.gitkeep diff --git a/app/Language/.gitkeep b/app/Language/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/Language/.gitkeep diff --git a/app/Libraries/.gitkeep b/app/Libraries/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/Libraries/.gitkeep diff --git a/app/Models/.gitkeep b/app/Models/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/Models/.gitkeep diff --git a/app/ThirdParty/.gitkeep b/app/ThirdParty/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/ThirdParty/.gitkeep diff --git a/app/Views/errors/cli/error_404.php b/app/Views/errors/cli/error_404.php new file mode 100644 index 0000000..d5bccb4 --- /dev/null +++ b/app/Views/errors/cli/error_404.php @@ -0,0 +1,6 @@ + +Message: +Filename: getFile(), "\n"; ?> +Line Number: getLine(); ?> + + + + Backtrace: + getTrace() as $error): ?> + + + + + + diff --git a/app/Views/errors/cli/production.php b/app/Views/errors/cli/production.php new file mode 100644 index 0000000..7db744e --- /dev/null +++ b/app/Views/errors/cli/production.php @@ -0,0 +1,5 @@ + + + + + 404 Page Not Found + + + + +
+

404 - File Not Found

+ +

+ + + + Sorry! Cannot seem to find the page you were looking for. + +

+
+ + diff --git a/app/Views/errors/html/error_exception.php b/app/Views/errors/html/error_exception.php new file mode 100644 index 0000000..7052e53 --- /dev/null +++ b/app/Views/errors/html/error_exception.php @@ -0,0 +1,401 @@ + + + + + + + + <?= htmlspecialchars($title, ENT_SUBSTITUTE, 'UTF-8') ?> + + + + + + + +
+
+

getCode() ? ' #' . $exception->getCode() : '') ?>

+

+ getMessage() ?> + getMessage())) ?>" + rel="noreferrer" target="_blank">search → +

+
+
+ + +
+

at line

+ + +
+ +
+ +
+ +
+ + + +
+ + +
+ +
    + $row) : ?> + +
  1. +

    + + + + + {PHP internal code} + + + + +   —   + + + ( arguments ) +

    + + + getParameters(); + } + foreach ($row['args'] as $key => $value) : ?> + + + + + + +
    name : "#$key", ENT_SUBSTITUTE, 'UTF-8') ?>
    +
    + + () + + + + +   —   () + +

    + + + +
    + +
    + +
  2. + + +
+ +
+ + +
+ + + +

$

+ + + + + + + + + + $value) : ?> + + + + + + +
KeyValue
+ + + + ' . print_r($value, true) ?> + +
+ + + + + + +

Constants

+ + + + + + + + + + $value) : ?> + + + + + + +
KeyValue
+ + + + ' . print_r($value, true) ?> + +
+ +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Pathuri ?>
HTTP MethodgetMethod(true) ?>
IP AddressgetIPAddress() ?>
Is AJAX Request?isAJAX() ? 'yes' : 'no' ?>
Is CLI Request?isCLI() ? 'yes' : 'no' ?>
Is Secure Request?isSecure() ? 'yes' : 'no' ?>
User AgentgetUserAgent()->getAgentString() ?>
+ + + + + + + + +

$

+ + + + + + + + + + $value) : ?> + + + + + + +
KeyValue
+ + + + ' . print_r($value, true) ?> + +
+ + + + + +
+ No $_GET, $_POST, or $_COOKIE Information to show. +
+ + + + getHeaders(); ?> + + +

Headers

+ + + + + + + + + + $value) : ?> + + + + + + + + + + +
HeaderValue
getName(), 'html') ?>getValueLine(), 'html') ?>
+ + +
+ + + setStatusCode(http_response_code()); + ?> +
+ + + + + +
Response StatusgetStatusCode() . ' - ' . $response->getReason() ?>
+ + getHeaders(); ?> + + + +

Headers

+ + + + + + + + + + $value) : ?> + + + + + + +
HeaderValue
getHeaderLine($name), 'html') ?>
+ + +
+ + +
+ + +
    + +
  1. + +
+
+ + +
+ + + + + + + + + + + + + + + + +
Memory Usage
Peak Memory Usage:
Memory Limit:
+ +
+ +
+ +
+ + + + + diff --git a/app/Views/errors/html/production.php b/app/Views/errors/html/production.php new file mode 100644 index 0000000..cca49c2 --- /dev/null +++ b/app/Views/errors/html/production.php @@ -0,0 +1,25 @@ + + + + + + + Whoops! + + + + + +
+ +

Whoops!

+ +

We seem to have hit a snag. Please try again later...

+ +
+ + + + diff --git a/app/Views/welcome_message.php b/app/Views/welcome_message.php new file mode 100644 index 0000000..0c3281a --- /dev/null +++ b/app/Views/welcome_message.php @@ -0,0 +1,139 @@ + + + + Welcome to CodeIgniter + + + + + + + +
+ +

Welcome to CodeIgniter

+ +

version

+ + + +
+

The page you are looking at is being generated dynamically by CodeIgniter.

+ +

If you would like to edit this page you'll find it located at:

+ +
+				
+					application/Views/welcome_message.php
+				
+				
+ +

The corresponding controller for this page is found at:

+ +
+				
+					application/Controllers/Home.php
+				
+				
+ +

If you are exploring CodeIgniter for the very first time, you + should start by reading the + User Guide.

+ +
+ + + +
+ + + diff --git a/app/index.html b/app/index.html new file mode 100644 index 0000000..b702fbc --- /dev/null +++ b/app/index.html @@ -0,0 +1,11 @@ + + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + diff --git a/composer.json b/composer.json index d51327b..672c5f1 100644 --- a/composer.json +++ b/composer.json @@ -9,6 +9,7 @@ "ext-curl": "*", "ext-intl": "*", "kint-php/kint": "^2.1", + "psr/log": "^1.1", "zendframework/zend-escaper": "^2.5" }, "require-dev": { @@ -19,15 +20,13 @@ }, "autoload": { "psr-4": { - "CodeIgniter\\": "system/", - "Psr\\Log\\": "system/ThirdParty/PSR/Log/" + "CodeIgniter\\": "system/" } }, "scripts": { "post-update-cmd": [ "composer dump-autoload", - "CodeIgniter\\ComposerScripts::postUpdate", - "bash admin/setup.sh" + "CodeIgniter\\ComposerScripts::postUpdate" ] }, "support": { diff --git a/public/index.php b/public/index.php index 86d06ed..cd38b91 100644 --- a/public/index.php +++ b/public/index.php @@ -13,7 +13,7 @@ // Location of the Paths config file. // This is the line that might need to be changed, depending on your folder structure. -$pathsPath = FCPATH . '../application/Config/Paths.php'; +$pathsPath = FCPATH . '../app/Config/Paths.php'; /* *--------------------------------------------------------------- @@ -32,7 +32,7 @@ $paths = new Config\Paths(); // Location of the framework bootstrap file. -$app = require FCPATH . '../' . rtrim($paths->systemDirectory, '/ ') . '/bootstrap.php'; +$app = require rtrim($paths->systemDirectory, '/ ') . '/bootstrap.php'; /* *--------------------------------------------------------------- diff --git a/spark b/spark index 7ce369b..8437468 100755 --- a/spark +++ b/spark @@ -29,20 +29,16 @@ die("The cli tool is not supported when running php-cgi. It needs php-cli to function!\n\n"); } -// Location to the Paths config file. -$pathsPath = 'application/Config/Paths.php'; +// Path to the front controller +define('FCPATH', __DIR__ . '/public' . DIRECTORY_SEPARATOR); // Load our paths config file -require $pathsPath; +require 'app/Config/Paths.php'; + $paths = new Config\Paths(); -$public = trim($paths->publicDirectory, '/'); - -// Path to the front controller -define('FCPATH', realpath($public) . DIRECTORY_SEPARATOR); - // Ensure the current directory is pointing to the front controller's directory -chdir($public); +chdir(FCPATH); $app = require rtrim($paths->systemDirectory, '/ ') . '/bootstrap.php'; diff --git a/system/Autoloader/Autoloader.php b/system/Autoloader/Autoloader.php index 4735119..3baf97f 100644 --- a/system/Autoloader/Autoloader.php +++ b/system/Autoloader/Autoloader.php @@ -76,7 +76,6 @@ */ class Autoloader { - /** * Stores namespaces as key, and path as values. * @@ -98,6 +97,8 @@ * the valid parts that we'll need. * * @param \Config\Autoload $config + * + * @return $this */ public function initialize(\Config\Autoload $config) { @@ -110,7 +111,7 @@ if (isset($config->psr4)) { - $this->prefixes = $config->psr4; + $this->addNamespace($config->psr4); } if (isset($config->classmap)) @@ -119,14 +120,14 @@ } unset($config); + + return $this; } //-------------------------------------------------------------------- /** * Register the loader with the SPL autoloader stack. - * - * @codeCoverageIgnore */ public function register() { @@ -144,41 +145,51 @@ $config = is_array($this->classmap) ? $this->classmap : []; spl_autoload_register(function ($class) use ($config) { - if (! array_key_exists($class, $config)) + if (empty($config[$class])) { return false; } include_once $config[$class]; }, true, // Throw exception - true // Prepend + true // Prepend ); } //-------------------------------------------------------------------- /** - * Registers a namespace with the autoloader. + * Registers namespaces with the autoloader. * - * @param string $namespace - * @param string $path + * @param array|string $namespace + * @param string $path * * @return Autoloader */ - public function addNamespace(string $namespace, string $path) + public function addNamespace($namespace, string $path = null) { - if (isset($this->prefixes[$namespace])) + if (is_array($namespace)) { - if (is_string($this->prefixes[$namespace])) + foreach ($namespace as $prefix => $path) { - $this->prefixes[$namespace] = [$this->prefixes[$namespace]]; - } + $prefix = trim($prefix, '\\'); - $this->prefixes[$namespace] = array_merge($this->prefixes[$namespace], [$path]); + if (is_array($path)) + { + foreach ($path as $dir) + { + $this->prefixes[$prefix][] = rtrim($dir, '/') . '/'; + } + + continue; + } + + $this->prefixes[$prefix][] = rtrim($path, '/') . '/'; + } } else { - $this->prefixes[$namespace] = [$path]; + $this->prefixes[trim($namespace, '\\')][] = rtrim($path, '/') . '/'; } return $this; @@ -187,6 +198,27 @@ //-------------------------------------------------------------------- /** + * Get namespaces with prefixes as keys and paths as values. + * + * If a prefix param is set, returns only paths to the given prefix. + * + * @var string|null $prefix + * + * @return array + */ + public function getNamespace(string $prefix = null) + { + if ($prefix === null) + { + return $this->prefixes; + } + + return $this->prefixes[trim($prefix, '\\')] ?? []; + } + + //-------------------------------------------------------------------- + + /** * Removes a single namespace from the psr4 settings. * * @param string $namespace @@ -195,7 +227,7 @@ */ public function removeNamespace(string $namespace) { - unset($this->prefixes[$namespace]); + unset($this->prefixes[trim($namespace, '\\')]); return $this; } @@ -207,7 +239,7 @@ * * @param string $class The fully qualified class name. * - * @return mixed The mapped file on success, or boolean false + * @return string|false The mapped file on success, or boolean false * on failure. */ public function loadClass(string $class) @@ -234,7 +266,7 @@ * * @param string $class The fully-qualified class name * - * @return mixed The mapped file name on success, or boolean false on fail + * @return string|false The mapped file name on success, or boolean false on fail */ protected function loadInNamespace(string $class) { @@ -245,18 +277,14 @@ foreach ($this->prefixes as $namespace => $directories) { - if (is_string($directories)) - { - $directories = [$directories]; - } - foreach ($directories as $directory) { $directory = rtrim($directory, '/'); if (strpos($class, $namespace) === 0) { - $filePath = $directory . str_replace('\\', '/', substr($class, strlen($namespace))) . '.php'; + $filePath = $directory . str_replace('\\', '/', + substr($class, strlen($namespace))) . '.php'; $filename = $this->requireFile($filePath); if ($filename) @@ -316,11 +344,9 @@ * A central way to require a file is loaded. Split out primarily * for testing purposes. * - * @codeCoverageIgnore - * * @param string $file * - * @return boolean + * @return string|false The filename on success, false if the file is not loaded */ protected function requireFile(string $file) { @@ -365,6 +391,5 @@ return $filename; } - //-------------------------------------------------------------------- } diff --git a/system/Autoloader/FileLocator.php b/system/Autoloader/FileLocator.php index fb2893f..9cc714a 100644 --- a/system/Autoloader/FileLocator.php +++ b/system/Autoloader/FileLocator.php @@ -36,10 +36,8 @@ * @filesource */ -use Config\Autoload; - /** - * Class Loader + * Class FileLocator * * Allows loading non-class files in a namespaced manner. * Works with Helpers, Views, etc. @@ -48,29 +46,21 @@ */ class FileLocator { - /** - * Stores our namespaces - * - * @var array + * @var \CodeIgniter\Autoloader\Autoloader */ - protected $namespaces; + protected $autoloader; //-------------------------------------------------------------------- /** * Constructor * - * @param Autoload $autoload + * @param Autoloader $autoloader */ - public function __construct(Autoload $autoload) + public function __construct(Autoloader $autoloader) { - $this->namespaces = $autoload->psr4; - - unset($autoload); - - // Always keep the Application directory as a "package". - array_unshift($this->namespaces, APPPATH); + $this->autoloader = $autoloader; } //-------------------------------------------------------------------- @@ -83,27 +73,26 @@ * @param string $folder The folder within the namespace that we should look for the file. * @param string $ext The file extension the file should have. * - * @return string The path to the file if found, or an empty string. + * @return string|false The path to the file, or false if not found. */ - public function locateFile(string $file, string $folder = null, string $ext = 'php'): string + public function locateFile(string $file, string $folder = null, string $ext = 'php') { - // Ensure the extension is on the filename - $file = strpos($file, '.' . $ext) !== false ? $file : $file . '.' . $ext; + $file = $this->ensureExt($file, $ext); - // Clean the folder name from the filename - if (! empty($folder)) + // Clears the folder name if it is at the beginning of the filename + if (! empty($folder) && ($pos = strpos($file, $folder)) === 0) { - $file = str_replace($folder . '/', '', $file); + $file = substr($file, strlen($folder . '/')); } - // No namespaceing? Try the application folder. + // Is not namespaced? Try the application folder. if (strpos($file, '\\') === false) { return $this->legacyLocate($file, $folder); } // Standardize slashes to handle nested directories. - $file = str_replace('/', '\\', $file); + $file = strtr($file, '/', '\\'); $segments = explode('\\', $file); @@ -117,16 +106,20 @@ $prefix = ''; $filename = ''; + // Namespaces always comes with arrays of paths + $namespaces = $this->autoloader->getNamespace(); + while (! empty($segments)) { - $prefix .= empty($prefix) ? ucfirst(array_shift($segments)) : '\\' . ucfirst(array_shift($segments)); + $prefix .= empty($prefix) + ? ucfirst(array_shift($segments)) + : '\\' . ucfirst(array_shift($segments)); - if (! array_key_exists($prefix, $this->namespaces)) + if (empty($namespaces[$prefix])) { continue; } - - $path = $this->namespaces[$prefix] . '/'; + $path = $this->getNamespaces($prefix); $filename = implode('/', $segments); break; } @@ -134,19 +127,14 @@ // IF we have a folder name, then the calling function // expects this file to be within that folder, like 'Views', // or 'libraries'. - if (! empty($folder) && strpos($filename, $folder) === false) + if (! empty($folder) && strpos($path . $filename, '/' . $folder . '/') === false) { $filename = $folder . '/' . $filename; } $path .= $filename; - if (! $this->requireFile($path)) - { - $path = ''; - } - - return $path; + return is_file($path) ? $path : false; } //-------------------------------------------------------------------- @@ -224,18 +212,15 @@ */ public function search(string $path, string $ext = 'php'): array { + $path = $this->ensureExt($path, $ext); + $foundPaths = []; - // Ensure the extension is on the filename - $path = strpos($path, '.' . $ext) !== false ? $path : $path . '.' . $ext; - - foreach ($this->namespaces as $name => $folder) + foreach ($this->getNamespaces() as $namespace) { - $folder = rtrim($folder, '/') . '/'; - - if (is_file($folder . $path) === true) + if (is_file($namespace['path'] . $path)) { - $foundPaths[] = $folder . $path; + $foundPaths[] = $namespace['path'] . $path; } } @@ -248,13 +233,69 @@ //-------------------------------------------------------------------- /** - * Attempts to load a file and instantiate a new class by looking - * at its full path and comparing that to our existing psr4 namespaces - * in Autoloader config file. + * Ensures a extension is at the end of a filename + * + * @param string $path + * @param string $ext + * + * @return string + */ + protected function ensureExt(string $path, string $ext): string + { + if ($ext) + { + $ext = '.' . $ext; + + if (substr($path, -strlen($ext)) !== $ext) + { + $path .= $ext; + } + } + + return $path; + } + + //-------------------------------------------------------------------- + + /** + * @param string|null $prefix + * + * @return array|string + */ + protected function getNamespaces(string $prefix = null) + { + if ($prefix) + { + $path = $this->autoloader->getNamespace($prefix); + + return isset($path[0]) ? $path[0] : ''; + } + + $namespaces = []; + + foreach ($this->autoloader->getNamespace() as $prefix => $paths) + { + foreach ($paths as $path) + { + $namespaces[] = [ + 'prefix' => $prefix, + 'path' => $path, + ]; + } + } + + return $namespaces; + } + + //-------------------------------------------------------------------- + + /** + * Find the qualified name of a file according to + * the namespace of the first matched namespace path. * * @param string $path * - * @return string|void + * @return string|false The qualified name or false if the path is not found */ public function findQualifiedNameFromPath(string $path) { @@ -262,34 +303,39 @@ if (! $path) { - return; + return false; } - foreach ($this->namespaces as $namespace => $nsPath) + foreach ($this->getNamespaces() as $namespace) { - $nsPath = realpath($nsPath); - if (is_numeric($namespace) || empty($nsPath)) + $namespace['path'] = realpath($namespace['path']); + + if (empty($namespace['path'])) { continue; } - if (mb_strpos($path, $nsPath) === 0) + if (mb_strpos($path, $namespace['path']) === 0) { - $className = '\\' . $namespace . '\\' . - ltrim(str_replace('/', '\\', mb_substr($path, mb_strlen($nsPath))), '\\'); + $className = '\\' . $namespace['prefix'] . '\\' . + ltrim(str_replace('/', '\\', mb_substr( + $path, mb_strlen($namespace['path'])) + ), '\\'); // Remove the file extension (.php) $className = mb_substr($className, 0, -4); return $className; } } + + return false; } //-------------------------------------------------------------------- /** * Scans the defined namespaces, returning a list of all files - * that are contained within the subpath specifed by $path. + * that are contained within the subpath specified by $path. * * @param string $path * @@ -305,9 +351,9 @@ $files = []; helper('filesystem'); - foreach ($this->namespaces as $namespace => $nsPath) + foreach ($this->getNamespaces() as $namespace) { - $fullPath = realpath(rtrim($nsPath, '/') . '/' . $path); + $fullPath = realpath($namespace['path'] . $path); if (! is_dir($fullPath)) { @@ -325,6 +371,8 @@ return $files; } + //-------------------------------------------------------------------- + /** * Checks the application folder to see if the file can be found. * Only for use with filenames that DO NOT include namespacing. @@ -332,44 +380,25 @@ * @param string $file * @param string|null $folder * - * @return string - * @internal param string $ext + * @return string|false The path to the file, or false if not found. */ - protected function legacyLocate(string $file, string $folder = null): string + protected function legacyLocate(string $file, string $folder = null) { $paths = [ APPPATH, - BASEPATH, + SYSTEMPATH, ]; foreach ($paths as $path) { $path .= empty($folder) ? $file : $folder . '/' . $file; - if ($this->requireFile($path) === true) + if (is_file($path)) { return $path; } } - return ''; + return false; } - - //-------------------------------------------------------------------- - - /** - * Checks to see if a file exists on the file system. This is split - * out to it's own method to make testing simpler. - * - * @codeCoverageIgnore - * @param string $path - * - * @return boolean - */ - protected function requireFile(string $path): bool - { - return is_file($path); - } - - //-------------------------------------------------------------------- } diff --git a/system/CLI/CommandRunner.php b/system/CLI/CommandRunner.php index 7585aa9..bca0980 100644 --- a/system/CLI/CommandRunner.php +++ b/system/CLI/CommandRunner.php @@ -37,6 +37,7 @@ * @filesource */ +use CodeIgniter\Config\Services; use CodeIgniter\Controller; class CommandRunner extends Controller @@ -109,7 +110,7 @@ { if (! isset($this->commands[$command])) { - CLI::error('Command \'' . $command . '\' not found'); + CLI::error(lang('CLI.commandNotFound', [$command])); CLI::newLine(); return; } @@ -127,12 +128,10 @@ /** * Scans all Commands directories and prepares a list * of each command with it's group and file. - * - * @return null|void */ protected function createCommandList() { - $files = service('locator')->listFiles('Commands/'); + $files = Services::locator()->listFiles('Commands/'); // If no matching command files were found, bail if (empty($files)) @@ -144,7 +143,7 @@ // alias exists in the class. If so, return it. Otherwise, try the next. foreach ($files as $file) { - $className = service('locator')->findQualifiedNameFromPath($file); + $className = Services::locator()->findQualifiedNameFromPath($file); if (empty($className) || ! class_exists($className)) { continue; diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index ad0fe18..81d4acb 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -61,7 +61,7 @@ /** * The current version of CodeIgniter Framework */ - const CI_VERSION = '4.0.0-alpha.3'; + const CI_VERSION = '4.0.0-alpha.4'; /** * App startup time. @@ -182,7 +182,7 @@ if (CI_DEBUG) { - require_once BASEPATH . 'ThirdParty/Kint/kint.php'; + require_once SYSTEMPATH . 'ThirdParty/Kint/kint.php'; } } @@ -303,7 +303,7 @@ $possibleRedirect = $filters->run($uri, 'before'); if ($possibleRedirect instanceof RedirectResponse) { - return $possibleRedirect; + return $possibleRedirect->send(); } // If a Response instance is returned, the Response will be sent back to the client and script execution will stop if ($possibleRedirect instanceof ResponseInterface) diff --git a/system/Common.php b/system/Common.php index 1814c88..7cda2ee 100644 --- a/system/Common.php +++ b/system/Common.php @@ -69,7 +69,7 @@ */ function cache(string $key = null) { - $cache = \Config\Services::cache(); + $cache = Services::cache(); // No params - return cache object if (is_null($key)) @@ -199,7 +199,7 @@ case 'empty': return ''; case 'null': - return; + return null; } return $value; @@ -298,7 +298,7 @@ */ function session($val = null) { - $session = \Config\Services::session(); + $session = Services::session(); // Returning a single item? if (is_string($val)) @@ -325,7 +325,7 @@ */ function timer(string $name = null) { - $timer = \Config\Services::timer(); + $timer = Services::timer(); if (empty($name)) { @@ -488,9 +488,7 @@ */ function route_to(string $method, ...$params): string { - $routes = Services::routes(); - - return $routes->reverseRoute($method, ...$params); + return Services::routes()->reverseRoute($method, ...$params); } } @@ -581,9 +579,11 @@ { if (strpos($path, APPPATH) === 0) { + // @codeCoverageIgnoreStart $appHelper = $path; + // @codeCoverageIgnoreEnd } - elseif (strpos($path, BASEPATH) === 0) + elseif (strpos($path, SYSTEMPATH) === 0) { $systemHelper = $path; } @@ -597,7 +597,9 @@ // App-level helpers should override all others if (! empty($appHelper)) { + // @codeCoverageIgnoreStart $includes[] = $appHelper; + // @codeCoverageIgnoreEnd } // All namespaced files get added in next @@ -688,9 +690,9 @@ * * @return string */ - function csrf_field() + function csrf_field(string $id = null) { - return ''; + return ''; } } @@ -713,7 +715,7 @@ * * Not testable, as it will exit! * - * @throws \CodeIgniter\HTTP\RedirectException + * @throws \CodeIgniter\HTTP\Exceptions\HTTPException * @codeCoverageIgnore */ function force_https(int $duration = 31536000, RequestInterface $request = null, ResponseInterface $response = null) diff --git a/system/ComposerScripts.php b/system/ComposerScripts.php index 87bbc2e..7cd91e3 100644 --- a/system/ComposerScripts.php +++ b/system/ComposerScripts.php @@ -49,7 +49,7 @@ */ class ComposerScripts { - protected static $basePath = 'system/ThirdParty/'; + protected static $basePath = 'ThirdParty/'; /** * After composer install/update, this is called to move @@ -144,7 +144,7 @@ { if (class_exists('\\Zend\\Escaper\\Escaper') && is_file(static::getClassFilePath('\\Zend\\Escaper\\Escaper'))) { - $base = static::$basePath . 'ZendEscaper'; + $base = basename(__DIR__) . '/' . static::$basePath . 'ZendEscaper'; foreach ([$base, $base . '/Exception'] as $path) { @@ -183,7 +183,7 @@ if (is_file($filename)) { - $base = static::$basePath . 'Kint'; + $base = basename(__DIR__) . '/' . static::$basePath . 'Kint'; // Remove the contents of the previous Kint folder, if any. if (is_dir($base)) diff --git a/system/Config/AutoloadConfig.php b/system/Config/AutoloadConfig.php index 1ec59d0..61ef260 100644 --- a/system/Config/AutoloadConfig.php +++ b/system/Config/AutoloadConfig.php @@ -88,7 +88,7 @@ * `]; */ $this->psr4 = [ - 'CodeIgniter' => realpath(BASEPATH), + 'CodeIgniter' => realpath(SYSTEMPATH), ]; if (isset($_SERVER['CI_ENVIRONMENT']) && $_SERVER['CI_ENVIRONMENT'] === 'testing') @@ -113,74 +113,78 @@ * ]; */ $this->classmap = [ - 'CodeIgniter\CodeIgniter' => BASEPATH . 'CodeIgniter.php', - 'CodeIgniter\CLI\CLI' => BASEPATH . 'CLI/CLI.php', - 'CodeIgniter\Cache\CacheFactory' => BASEPATH . 'Cache/CacheFactory.php', - 'CodeIgniter\Cache\CacheInterface' => BASEPATH . 'Cache/CacheInterface.php', - 'CodeIgniter\Cache\Handlers\DummyHandler' => BASEPATH . 'Cache/Handlers/DummyHandler.php', - 'CodeIgniter\Cache\Handlers\FileHandler' => BASEPATH . 'Cache/Handlers/FileHandler.php', - 'CodeIgniter\Cache\Handlers\MemcachedHandler' => BASEPATH . 'Cache/Handlers/MemcachedHandler.php', - 'CodeIgniter\Cache\Handlers\PredisHandler' => BASEPATH . 'Cache/Handlers/PredisHandler.php', - 'CodeIgniter\Cache\Handlers\RedisHandler' => BASEPATH . 'Cache/Handlers/RedisHandler.php', - 'CodeIgniter\Cache\Handlers\WincacheHandler' => BASEPATH . 'Cache/Handlers/WincacheHandler.php', - 'CodeIgniter\Controller' => BASEPATH . 'Controller.php', - 'CodeIgniter\Config\AutoloadConfig' => BASEPATH . 'Config/Autoload.php', - 'CodeIgniter\Config\BaseConfig' => BASEPATH . 'Config/BaseConfig.php', - 'CodeIgniter\Config\Database' => BASEPATH . 'Config/Database.php', - 'CodeIgniter\Config\Database\Connection' => BASEPATH . 'Config/Database/Connection.php', - 'CodeIgniter\Config\Database\Connection\MySQLi' => BASEPATH . 'Config/Database/Connection/MySQLi.php', - 'CodeIgniter\Config\DotEnv' => BASEPATH . 'Config/DotEnv.php', - 'CodeIgniter\Database\BaseBuilder' => BASEPATH . 'Database/BaseBuilder.php', - 'CodeIgniter\Database\BaseConnection' => BASEPATH . 'Database/BaseConnection.php', - 'CodeIgniter\Database\BaseResult' => BASEPATH . 'Database/BaseResult.php', - 'CodeIgniter\Database\Config' => BASEPATH . 'Database/Config.php', - 'CodeIgniter\Database\ConnectionInterface' => BASEPATH . 'Database/ConnectionInterface.php', - 'CodeIgniter\Database\Database' => BASEPATH . 'Database/Database.php', - 'CodeIgniter\Database\Query' => BASEPATH . 'Database/Query.php', - 'CodeIgniter\Database\QueryInterface' => BASEPATH . 'Database/QueryInterface.php', - 'CodeIgniter\Database\ResultInterface' => BASEPATH . 'Database/ResultInterface.php', - 'CodeIgniter\Database\Migration' => BASEPATH . 'Database/Migration.php', - 'CodeIgniter\Database\MigrationRunner' => BASEPATH . 'Database/MigrationRunner.php', - 'CodeIgniter\Debug\Exceptions' => BASEPATH . 'Debug/Exceptions.php', - 'CodeIgniter\Debug\Timer' => BASEPATH . 'Debug/Timer.php', - 'CodeIgniter\Debug\Iterator' => BASEPATH . 'Debug/Iterator.php', - 'CodeIgniter\Events\Events' => BASEPATH . 'Events/Events.php', - 'CodeIgniter\HTTP\CLIRequest' => BASEPATH . 'HTTP/CLIRequest.php', - 'CodeIgniter\HTTP\ContentSecurityPolicy' => BASEPATH . 'HTTP/ContentSecurityPolicy.php', - 'CodeIgniter\HTTP\CURLRequest' => BASEPATH . 'HTTP/CURLRequest.php', - 'CodeIgniter\HTTP\IncomingRequest' => BASEPATH . 'HTTP/IncomingRequest.php', - 'CodeIgniter\HTTP\Message' => BASEPATH . 'HTTP/Message.php', - 'CodeIgniter\HTTP\Negotiate' => BASEPATH . 'HTTP/Negotiate.php', - 'CodeIgniter\HTTP\Request' => BASEPATH . 'HTTP/Request.php', - 'CodeIgniter\HTTP\RequestInterface' => BASEPATH . 'HTTP/RequestInterface.php', - 'CodeIgniter\HTTP\Response' => BASEPATH . 'HTTP/Response.php', - 'CodeIgniter\HTTP\ResponseInterface' => BASEPATH . 'HTTP/ResponseInterface.php', - 'CodeIgniter\HTTP\URI' => BASEPATH . 'HTTP/URI.php', - 'CodeIgniter\Log\Logger' => BASEPATH . 'Log/Logger.php', - 'Psr\Log\LoggerAwareInterface' => BASEPATH . 'ThirdParty/PSR/Log/LoggerAwareInterface.php', - 'Psr\Log\LoggerAwareTrait' => BASEPATH . 'ThirdParty/PSR/Log/LoggerAwareTrait.php', - 'Psr\Log\LoggerInterface' => BASEPATH . 'ThirdParty/PSR/Log/LoggerInterface.php', - 'Psr\Log\LogLevel' => BASEPATH . 'ThirdParty/PSR/Log/LogLevel.php', - 'CodeIgniter\Log\Handlers\BaseHandler' => BASEPATH . 'Log/Handlers/BaseHandler.php', - 'CodeIgniter\Log\Handlers\ChromeLoggerHandler' => BASEPATH . 'Log/Handlers/ChromeLoggerHandler.php', - 'CodeIgniter\Log\Handlers\FileHandler' => BASEPATH . 'Log/Handlers/FileHandler.php', - 'CodeIgniter\Log\Handlers\HandlerInterface' => BASEPATH . 'Log/Handlers/HandlerInterface.php', - 'CodeIgniter\Router\RouteCollection' => BASEPATH . 'Router/RouteCollection.php', - 'CodeIgniter\Router\RouteCollectionInterface' => BASEPATH . 'Router/RouteCollectionInterface.php', - 'CodeIgniter\Router\Router' => BASEPATH . 'Router/Router.php', - 'CodeIgniter\Router\RouterInterface' => BASEPATH . 'Router/RouterInterface.php', - 'CodeIgniter\Security\Security' => BASEPATH . 'Security/Security.php', - 'CodeIgniter\Session\Session' => BASEPATH . 'Session/Session.php', - 'CodeIgniter\Session\SessionInterface' => BASEPATH . 'Session/SessionInterface.php', - 'CodeIgniter\Session\Handlers\BaseHandler' => BASEPATH . 'Session/Handlers/BaseHandler.php', - 'CodeIgniter\Session\Handlers\FileHandler' => BASEPATH . 'Session/Handlers/FileHandler.php', - 'CodeIgniter\Session\Handlers\MemcachedHandler' => BASEPATH . 'Session/Handlers/MemcachedHandler.php', - 'CodeIgniter\Session\Handlers\RedisHandler' => BASEPATH . 'Session/Handlers/RedisHandler.php', - 'CodeIgniter\View\RendererInterface' => BASEPATH . 'View/RendererInterface.php', - 'CodeIgniter\View\View' => BASEPATH . 'View/View.php', - 'CodeIgniter\View\Parser' => BASEPATH . 'View/Parser.php', - 'CodeIgniter\View\Cell' => BASEPATH . 'View/Cell.php', - 'Zend\Escaper\Escaper' => BASEPATH . 'ThirdParty/ZendEscaper/Escaper.php', + 'CodeIgniter\CodeIgniter' => SYSTEMPATH . 'CodeIgniter.php', + 'CodeIgniter\CLI\CLI' => SYSTEMPATH . 'CLI/CLI.php', + 'CodeIgniter\Cache\CacheFactory' => SYSTEMPATH . 'Cache/CacheFactory.php', + 'CodeIgniter\Cache\CacheInterface' => SYSTEMPATH . 'Cache/CacheInterface.php', + 'CodeIgniter\Cache\Handlers\DummyHandler' => SYSTEMPATH . 'Cache/Handlers/DummyHandler.php', + 'CodeIgniter\Cache\Handlers\FileHandler' => SYSTEMPATH . 'Cache/Handlers/FileHandler.php', + 'CodeIgniter\Cache\Handlers\MemcachedHandler' => SYSTEMPATH . 'Cache/Handlers/MemcachedHandler.php', + 'CodeIgniter\Cache\Handlers\PredisHandler' => SYSTEMPATH . 'Cache/Handlers/PredisHandler.php', + 'CodeIgniter\Cache\Handlers\RedisHandler' => SYSTEMPATH . 'Cache/Handlers/RedisHandler.php', + 'CodeIgniter\Cache\Handlers\WincacheHandler' => SYSTEMPATH . 'Cache/Handlers/WincacheHandler.php', + 'CodeIgniter\Controller' => SYSTEMPATH . 'Controller.php', + 'CodeIgniter\Config\AutoloadConfig' => SYSTEMPATH . 'Config/Autoload.php', + 'CodeIgniter\Config\BaseConfig' => SYSTEMPATH . 'Config/BaseConfig.php', + 'CodeIgniter\Config\Database' => SYSTEMPATH . 'Config/Database.php', + 'CodeIgniter\Config\Database\Connection' => SYSTEMPATH . 'Config/Database/Connection.php', + 'CodeIgniter\Config\Database\Connection\MySQLi' => SYSTEMPATH . 'Config/Database/Connection/MySQLi.php', + 'CodeIgniter\Config\DotEnv' => SYSTEMPATH . 'Config/DotEnv.php', + 'CodeIgniter\Database\BaseBuilder' => SYSTEMPATH . 'Database/BaseBuilder.php', + 'CodeIgniter\Database\BaseConnection' => SYSTEMPATH . 'Database/BaseConnection.php', + 'CodeIgniter\Database\BaseResult' => SYSTEMPATH . 'Database/BaseResult.php', + 'CodeIgniter\Database\Config' => SYSTEMPATH . 'Database/Config.php', + 'CodeIgniter\Database\ConnectionInterface' => SYSTEMPATH . 'Database/ConnectionInterface.php', + 'CodeIgniter\Database\Database' => SYSTEMPATH . 'Database/Database.php', + 'CodeIgniter\Database\Query' => SYSTEMPATH . 'Database/Query.php', + 'CodeIgniter\Database\QueryInterface' => SYSTEMPATH . 'Database/QueryInterface.php', + 'CodeIgniter\Database\ResultInterface' => SYSTEMPATH . 'Database/ResultInterface.php', + 'CodeIgniter\Database\Migration' => SYSTEMPATH . 'Database/Migration.php', + 'CodeIgniter\Database\MigrationRunner' => SYSTEMPATH . 'Database/MigrationRunner.php', + 'CodeIgniter\Debug\Exceptions' => SYSTEMPATH . 'Debug/Exceptions.php', + 'CodeIgniter\Debug\Timer' => SYSTEMPATH . 'Debug/Timer.php', + 'CodeIgniter\Debug\Iterator' => SYSTEMPATH . 'Debug/Iterator.php', + 'CodeIgniter\Events\Events' => SYSTEMPATH . 'Events/Events.php', + 'CodeIgniter\HTTP\CLIRequest' => SYSTEMPATH . 'HTTP/CLIRequest.php', + 'CodeIgniter\HTTP\ContentSecurityPolicy' => SYSTEMPATH . 'HTTP/ContentSecurityPolicy.php', + 'CodeIgniter\HTTP\CURLRequest' => SYSTEMPATH . 'HTTP/CURLRequest.php', + 'CodeIgniter\HTTP\IncomingRequest' => SYSTEMPATH . 'HTTP/IncomingRequest.php', + 'CodeIgniter\HTTP\Message' => SYSTEMPATH . 'HTTP/Message.php', + 'CodeIgniter\HTTP\Negotiate' => SYSTEMPATH . 'HTTP/Negotiate.php', + 'CodeIgniter\HTTP\Request' => SYSTEMPATH . 'HTTP/Request.php', + 'CodeIgniter\HTTP\RequestInterface' => SYSTEMPATH . 'HTTP/RequestInterface.php', + 'CodeIgniter\HTTP\Response' => SYSTEMPATH . 'HTTP/Response.php', + 'CodeIgniter\HTTP\ResponseInterface' => SYSTEMPATH . 'HTTP/ResponseInterface.php', + 'CodeIgniter\HTTP\URI' => SYSTEMPATH . 'HTTP/URI.php', + 'CodeIgniter\Log\Logger' => SYSTEMPATH . 'Log/Logger.php', + 'Psr\Log\AbstractLogger' => SYSTEMPATH . 'ThirdParty/PSR/Log/AbstractLogger.php', + 'Psr\Log\InvalidArgumentException' => SYSTEMPATH . 'ThirdParty/PSR/Log/InvalidArgumentException.php', + 'Psr\Log\LoggerAwareInterface' => SYSTEMPATH . 'ThirdParty/PSR/Log/LoggerAwareInterface.php', + 'Psr\Log\LoggerAwareTrait' => SYSTEMPATH . 'ThirdParty/PSR/Log/LoggerAwareTrait.php', + 'Psr\Log\LoggerInterface' => SYSTEMPATH . 'ThirdParty/PSR/Log/LoggerInterface.php', + 'Psr\Log\LoggerTrait' => SYSTEMPATH . 'ThirdParty/PSR/Log/LoggerTrait.php', + 'Psr\Log\LogLevel' => SYSTEMPATH . 'ThirdParty/PSR/Log/LogLevel.php', + 'Psr\Log\NullLogger' => SYSTEMPATH . 'ThirdParty/PSR/Log/NullLogger.php', + 'CodeIgniter\Log\Handlers\BaseHandler' => SYSTEMPATH . 'Log/Handlers/BaseHandler.php', + 'CodeIgniter\Log\Handlers\ChromeLoggerHandler' => SYSTEMPATH . 'Log/Handlers/ChromeLoggerHandler.php', + 'CodeIgniter\Log\Handlers\FileHandler' => SYSTEMPATH . 'Log/Handlers/FileHandler.php', + 'CodeIgniter\Log\Handlers\HandlerInterface' => SYSTEMPATH . 'Log/Handlers/HandlerInterface.php', + 'CodeIgniter\Router\RouteCollection' => SYSTEMPATH . 'Router/RouteCollection.php', + 'CodeIgniter\Router\RouteCollectionInterface' => SYSTEMPATH . 'Router/RouteCollectionInterface.php', + 'CodeIgniter\Router\Router' => SYSTEMPATH . 'Router/Router.php', + 'CodeIgniter\Router\RouterInterface' => SYSTEMPATH . 'Router/RouterInterface.php', + 'CodeIgniter\Security\Security' => SYSTEMPATH . 'Security/Security.php', + 'CodeIgniter\Session\Session' => SYSTEMPATH . 'Session/Session.php', + 'CodeIgniter\Session\SessionInterface' => SYSTEMPATH . 'Session/SessionInterface.php', + 'CodeIgniter\Session\Handlers\BaseHandler' => SYSTEMPATH . 'Session/Handlers/BaseHandler.php', + 'CodeIgniter\Session\Handlers\FileHandler' => SYSTEMPATH . 'Session/Handlers/FileHandler.php', + 'CodeIgniter\Session\Handlers\MemcachedHandler' => SYSTEMPATH . 'Session/Handlers/MemcachedHandler.php', + 'CodeIgniter\Session\Handlers\RedisHandler' => SYSTEMPATH . 'Session/Handlers/RedisHandler.php', + 'CodeIgniter\View\RendererInterface' => SYSTEMPATH . 'View/RendererInterface.php', + 'CodeIgniter\View\View' => SYSTEMPATH . 'View/View.php', + 'CodeIgniter\View\Parser' => SYSTEMPATH . 'View/Parser.php', + 'CodeIgniter\View\Cell' => SYSTEMPATH . 'View/Cell.php', + 'Zend\Escaper\Escaper' => SYSTEMPATH . 'ThirdParty/ZendEscaper/Escaper.php', ]; if (isset($_SERVER['CI_ENVIRONMENT']) && $_SERVER['CI_ENVIRONMENT'] === 'testing') diff --git a/system/Config/BaseService.php b/system/Config/BaseService.php index 4da1518..a9e5bba 100644 --- a/system/Config/BaseService.php +++ b/system/Config/BaseService.php @@ -119,6 +119,31 @@ //-------------------------------------------------------------------- /** + * The Autoloader class is the central class that handles our + * spl_autoload_register method, and helper methods. + * + * @param boolean $getShared + * + * @return \CodeIgniter\Autoloader\Autoloader + */ + public static function autoloader(bool $getShared = true) + { + if ($getShared) + { + if (empty(static::$instances['autoloader'])) + { + static::$instances['autoloader'] = new \CodeIgniter\Autoloader\Autoloader(); + } + + return static::$instances['autoloader']; + } + + return new \CodeIgniter\Autoloader\Autoloader(); + } + + //-------------------------------------------------------------------- + + /** * The file locator provides utility methods for looking for non-classes * within namespaced folders, as well as convenience methods for * loading 'helpers', and 'libraries'. @@ -131,10 +156,17 @@ { if ($getShared) { - return static::getSharedInstance('locator'); + if (empty(static::$instances['locator'])) + { + static::$instances['locator'] = new \CodeIgniter\Autoloader\FileLocator( + static::autoloader() + ); + } + + return static::$instances['locator']; } - return new \CodeIgniter\Autoloader\FileLocator(new \Config\Autoload()); + return new \CodeIgniter\Autoloader\FileLocator(static::autoloader()); } //-------------------------------------------------------------------- @@ -164,12 +196,19 @@ /** * Reset shared instances and mocks for testing. + * + * @param boolean $init_autoloader Initializes autoloader instance */ - public static function reset() + public static function reset(bool $init_autoloader = false) { static::$mocks = []; static::$instances = []; + + if ($init_autoloader) + { + static::autoloader()->initialize(new \Config\Autoload()); + } } //-------------------------------------------------------------------- @@ -196,6 +235,8 @@ * * @param string $name * @param array $arguments + * + * @return mixed */ protected static function discoverServices(string $name, array $arguments) { @@ -210,7 +251,7 @@ if (empty($files)) { - return; + return null; } // Get instances of all service classes and cache them locally. @@ -230,7 +271,7 @@ if (! static::$services) { - return; + return null; } // Try to find the desired service method @@ -241,5 +282,7 @@ return $class::$name(...$arguments); } } + + return null; } } diff --git a/system/Config/Services.php b/system/Config/Services.php index 7f72403..3815d80 100644 --- a/system/Config/Services.php +++ b/system/Config/Services.php @@ -61,26 +61,6 @@ class Services extends BaseService { /** - * The Autoloader class is the central class that handles our - * spl_autoload_register method, and helper methods. - * - * @param boolean $getShared - * - * @return \CodeIgniter\Autoloader\Autoloader - */ - public static function autoloader(bool $getShared = true) - { - if ($getShared) - { - return static::getSharedInstance('autoloader'); - } - - return new \CodeIgniter\Autoloader\Autoloader(); - } - - //-------------------------------------------------------------------- - - /** * The cache class provides a simple way to store and retrieve * complex data for later. * @@ -742,12 +722,12 @@ //-------------------------------------------------------------------- /** - * @param \Config\App $config - * @param boolean $getShared + * @param \Config\Toolbar $config + * @param boolean $getShared * * @return \CodeIgniter\Debug\Toolbar */ - public static function toolbar(\Config\App $config = null, bool $getShared = true) + public static function toolbar(\Config\Toolbar $config = null, bool $getShared = true) { if ($getShared) { @@ -756,7 +736,7 @@ if (! is_object($config)) { - $config = config(App::class); + $config = config(\Config\Toolbar::class); } return new \CodeIgniter\Debug\Toolbar($config); diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index 4e90e5e..fbfa3ed 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -36,7 +36,7 @@ * @filesource */ -use \CodeIgniter\Database\Exceptions\DatabaseException; +use CodeIgniter\Database\Exceptions\DatabaseException; /** * Class BaseBuilder @@ -1428,6 +1428,9 @@ if ($reset === true) { $this->resetSelect(); + + // Clear our binds so we don't eat up memory + $this->binds = []; } return $result; @@ -1776,7 +1779,12 @@ { $this->resetWrite(); - return $this->db->query($sql, $this->binds); + $result = $this->db->query($sql, $this->binds); + + // Clear our binds so we don't eat up memory + $this->binds = []; + + return $result; } } @@ -1975,6 +1983,9 @@ if ($this->db->query($sql, $this->binds)) { + // Clear our binds so we don't eat up memory + $this->binds = []; + return true; } diff --git a/system/Database/BaseUtils.php b/system/Database/BaseUtils.php index 4efa53e..81fe474 100644 --- a/system/Database/BaseUtils.php +++ b/system/Database/BaseUtils.php @@ -36,7 +36,7 @@ * @filesource */ -use \CodeIgniter\Database\Exceptions\DatabaseException; +use CodeIgniter\Database\Exceptions\DatabaseException; /** * Class BaseUtils diff --git a/system/Database/Forge.php b/system/Database/Forge.php index d2f4504..88cc304 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -36,7 +36,7 @@ * @filesource */ -use \CodeIgniter\Database\Exceptions\DatabaseException; +use CodeIgniter\Database\Exceptions\DatabaseException; /** * Class Forge diff --git a/system/Database/MigrationRunner.php b/system/Database/MigrationRunner.php index c190b1e..be7c50a 100644 --- a/system/Database/MigrationRunner.php +++ b/system/Database/MigrationRunner.php @@ -179,14 +179,14 @@ * Calls each migration step required to get to the schema version of * choice * - * @param integer $targetVersion Target schema version + * @param string $targetVersion Target schema version * @param string|null $namespace * @param string|null $group * * @return mixed TRUE if no migrations are found, current version string on success, FALSE on failure * @throws ConfigException */ - public function version(int $targetVersion, string $namespace = null, string $group = null) + public function version(string $targetVersion, string $namespace = null, string $group = null) { if (! $this->enabled) { @@ -277,12 +277,12 @@ /** * Sets the schema to the latest migration * - * @param string $namespace - * @param string $group + * @param string|null $namespace + * @param string|null $group * * @return mixed Current version string on success, FALSE on failure */ - public function latest($namespace = null, $group = null) + public function latest(string $namespace = null, string $group = null) { // Set Namespace if not null if (! is_null($namespace)) @@ -309,11 +309,11 @@ /** * Sets the schema to the latest migration for all namespaces * - * @param string $group + * @param string|null $group * * @return boolean */ - public function latestAll($group = null) + public function latestAll(string $group = null) { // Set database group if not null if (! is_null($group)) @@ -355,11 +355,11 @@ /** * Sets the (APP_NAMESPACE) schema to $currentVersion in migration config file * - * @param string $group + * @param string|null $group * * @return mixed TRUE if no migrations are found, current version string on success, FALSE on failure */ - public function current($group = null) + public function current(string $group = null) { // Set database group if not null if (! is_null($group)) @@ -417,13 +417,13 @@ * if sequential check if no gaps and check if all consistent with migrations table if downgrading * if timestamp check if consistent with migrations table if downgrading * - * @param array $migrations - * @param string $method - * @param integer $targetversion + * @param array $migrations + * @param string $method + * @param string $targetversion * * @return boolean */ - protected function checkMigrations(array $migrations, string $method, int $targetversion) + protected function checkMigrations(array $migrations, string $method, string $targetversion) { // Check if no migrations found if (empty($migrations)) @@ -436,7 +436,7 @@ } // Check if $targetversion file is found - if ($targetversion !== 0 && ! array_key_exists($targetversion, $migrations)) + if ($targetversion !== '0' && ! array_key_exists($targetversion, $migrations)) { if ($this->silent) { @@ -529,7 +529,7 @@ * * @return array */ - public function getHistory($group = 'default') + public function getHistory(string $group = 'default') { $query = $this->db->table($this->table) ->where('group', $group) @@ -571,7 +571,7 @@ * * @return string Numeric portion of a migration filename */ - protected function getMigrationNumber($migration) + protected function getMigrationNumber(string $migration) { return sscanf($migration, '%[0-9]+', $number) ? $number : '0'; } @@ -585,7 +585,7 @@ * * @return string text portion of a migration filename */ - protected function getMigrationName($migration) + protected function getMigrationName(string $migration) { $parts = explode('_', $migration); array_shift($parts); @@ -633,7 +633,7 @@ * * @internal param string $migration Migration reached */ - protected function addHistory($version) + protected function addHistory(string $version) { $this->db->table($this->table) ->insert([ @@ -656,7 +656,7 @@ * * @param string $version */ - protected function removeHistory($version) + protected function removeHistory(string $version) { $this->db->table($this->table) ->where('version', $version) diff --git a/system/Database/MySQLi/Connection.php b/system/Database/MySQLi/Connection.php index 96c2632..3fc4968 100644 --- a/system/Database/MySQLi/Connection.php +++ b/system/Database/MySQLi/Connection.php @@ -38,7 +38,7 @@ use CodeIgniter\Database\BaseConnection; use CodeIgniter\Database\ConnectionInterface; -use \CodeIgniter\Database\Exceptions\DatabaseException; +use CodeIgniter\Database\Exceptions\DatabaseException; /** * Connection for MySQLi @@ -170,42 +170,56 @@ } } - if ($this->mysqli->real_connect($hostname, $this->username, $this->password, - $this->database, $port, $socket, $client_flags) - ) + try { - // Prior to version 5.7.3, MySQL silently downgrades to an unencrypted connection if SSL setup fails - if (($client_flags & MYSQLI_CLIENT_SSL) && version_compare($this->mysqli->client_info, '5.7.3', '<=') && empty($this->mysqli->query("SHOW STATUS LIKE 'ssl_cipher'") - ->fetch_object()->Value) + if ($this->mysqli->real_connect($hostname, $this->username, $this->password, + $this->database, $port, $socket, $client_flags) ) { - $this->mysqli->close(); - $message = 'MySQLi was configured for an SSL connection, but got an unencrypted connection instead!'; - log_message('error', $message); - - if ($this->DBDebug) + // Prior to version 5.7.3, MySQL silently downgrades to an unencrypted connection if SSL setup fails + if (($client_flags & MYSQLI_CLIENT_SSL) && version_compare($this->mysqli->client_info, '5.7.3', '<=') + && empty($this->mysqli->query("SHOW STATUS LIKE 'ssl_cipher'") + ->fetch_object()->Value) + ) { - throw new DatabaseException($message); + $this->mysqli->close(); + $message = 'MySQLi was configured for an SSL connection, but got an unencrypted connection instead!'; + log_message('error', $message); + + if ($this->DBDebug) + { + throw new DatabaseException($message); + } + + return false; } - return false; - } - - if (! $this->mysqli->set_charset($this->charset)) - { - log_message('error', - "Database: Unable to set the configured connection charset ('{$this->charset}')."); - $this->mysqli->close(); - - if ($this->db->debug) + if (! $this->mysqli->set_charset($this->charset)) { - throw new DatabaseException('Unable to set client connection character set: ' . $this->charset); + log_message('error', + "Database: Unable to set the configured connection charset ('{$this->charset}')."); + $this->mysqli->close(); + + if ($this->db->debug) + { + throw new DatabaseException('Unable to set client connection character set: ' . $this->charset); + } + + return false; } - return false; + return $this->mysqli; } + } + catch (\Throwable $e) + { + // Clean sensitive information from errors. + $msg = $e->getMessage(); - return $this->mysqli; + $msg = str_replace($this->username, '****', $msg); + $msg = str_replace($this->password, '****', $msg); + + throw new \mysqli_sql_exception($msg, $e->getCode(), $e); } return false; @@ -343,7 +357,7 @@ */ public function affectedRows(): int { - return $this->connID->affected_rows; + return $this->connID->affected_rows ?? 0; } //-------------------------------------------------------------------- diff --git a/system/Database/MySQLi/PreparedQuery.php b/system/Database/MySQLi/PreparedQuery.php index f9f07dd..c470a15 100644 --- a/system/Database/MySQLi/PreparedQuery.php +++ b/system/Database/MySQLi/PreparedQuery.php @@ -37,7 +37,7 @@ */ use CodeIgniter\Database\PreparedQueryInterface; -use \CodeIgniter\Database\BasePreparedQuery; +use CodeIgniter\Database\BasePreparedQuery; class PreparedQuery extends BasePreparedQuery implements PreparedQueryInterface { diff --git a/system/Database/Postgre/Builder.php b/system/Database/Postgre/Builder.php index c697c6f..0cb11b9 100644 --- a/system/Database/Postgre/Builder.php +++ b/system/Database/Postgre/Builder.php @@ -37,7 +37,7 @@ */ use CodeIgniter\Database\BaseBuilder; -use \CodeIgniter\Database\Exceptions\DatabaseException; +use CodeIgniter\Database\Exceptions\DatabaseException; /** * Builder for Postgre diff --git a/system/Database/Postgre/PreparedQuery.php b/system/Database/Postgre/PreparedQuery.php index 874dc53..d28de33 100644 --- a/system/Database/Postgre/PreparedQuery.php +++ b/system/Database/Postgre/PreparedQuery.php @@ -37,7 +37,7 @@ */ use CodeIgniter\Database\PreparedQueryInterface; -use \CodeIgniter\Database\BasePreparedQuery; +use CodeIgniter\Database\BasePreparedQuery; class PreparedQuery extends BasePreparedQuery implements PreparedQueryInterface { diff --git a/system/Database/SQLite3/PreparedQuery.php b/system/Database/SQLite3/PreparedQuery.php index 0ad54ba..b0dd1a9 100644 --- a/system/Database/SQLite3/PreparedQuery.php +++ b/system/Database/SQLite3/PreparedQuery.php @@ -37,7 +37,7 @@ */ use CodeIgniter\Database\PreparedQueryInterface; -use \CodeIgniter\Database\BasePreparedQuery; +use CodeIgniter\Database\BasePreparedQuery; class PreparedQuery extends BasePreparedQuery implements PreparedQueryInterface { diff --git a/system/Debug/Exceptions.php b/system/Debug/Exceptions.php index b87fbd7..cb69f49 100644 --- a/system/Debug/Exceptions.php +++ b/system/Debug/Exceptions.php @@ -36,12 +36,14 @@ * @filesource */ +use CodeIgniter\API\ResponseTrait; + /** * Exceptions manager */ class Exceptions { - use \CodeIgniter\API\ResponseTrait; + use ResponseTrait; /** * Nesting level of the output buffering mechanism @@ -174,7 +176,8 @@ */ public function errorHandler(int $severity, string $message, string $file = null, int $line = null, $context = null) { - if (! (\error_reporting() & $severity)) { + if (! (\error_reporting() & $severity)) + { return; } @@ -357,9 +360,9 @@ { $file = 'APPPATH/' . substr($file, strlen(APPPATH)); } - elseif (strpos($file, BASEPATH) === 0) + elseif (strpos($file, SYSTEMPATH) === 0) { - $file = 'BASEPATH/' . substr($file, strlen(BASEPATH)); + $file = 'SYSTEMPATH/' . substr($file, strlen(SYSTEMPATH)); } elseif (strpos($file, FCPATH) === 0) { diff --git a/system/Debug/Toolbar.php b/system/Debug/Toolbar.php index 041e860..b4ecf54 100644 --- a/system/Debug/Toolbar.php +++ b/system/Debug/Toolbar.php @@ -36,7 +36,8 @@ * @filesource */ -use Config\App; +use CodeIgniter\Debug\Toolbar\Collectors\History; +use CodeIgniter\Format\JSONFormatter; use Config\Services; use CodeIgniter\Config\BaseConfig; use CodeIgniter\Format\XMLFormatter; @@ -52,21 +53,18 @@ */ class Toolbar { + /** + * @var BaseConfig + */ + protected $config; /** * Collectors to be used and displayed. * - * @var array + * @var \CodeIgniter\Debug\Toolbar\Collectors\BaseCollector[] */ protected $collectors = []; - /** - * Incoming Request - * - * @var \CodeIgniter\HTTP\IncomingRequest - */ - protected static $request; - //-------------------------------------------------------------------- /** @@ -76,12 +74,14 @@ */ public function __construct(BaseConfig $config) { - foreach ($config->toolbarCollectors as $collector) + $this->config = $config; + + foreach ($config->collectors as $collector) { if (! class_exists($collector)) { log_message('critical', 'Toolbar collector does not exists(' . $collector . ').' . - 'please check $toolbarCollectors in the Config\App.php file.'); + 'please check $collectors in the Config\Toolbar.php file.'); continue; } @@ -117,19 +117,7 @@ foreach ($this->collectors as $collector) { - $data['collectors'][] = [ - 'title' => $collector->getTitle(), - 'titleSafe' => $collector->getTitle(true), - 'titleDetails' => $collector->getTitleDetails(), - 'display' => $collector->display(), - 'badgeValue' => $collector->getBadgeValue(), - 'isEmpty' => $collector->isEmpty(), - 'hasTabContent' => $collector->hasTabContent(), - 'hasLabel' => $collector->hasLabel(), - 'icon' => $collector->icon(), - 'hasTimelineData' => $collector->hasTimelineData(), - 'timelineData' => $collector->timelineData(), - ]; + $data['collectors'][] = $collector->getAsArray(); } foreach ($this->collectVarData() as $heading => $items) @@ -208,98 +196,6 @@ //-------------------------------------------------------------------- - /** - * Format output - * - * @param string $data JSON encoded Toolbar data - * @param string $format html, json, xml - * - * @return string - */ - protected static function format(string $data, string $format = 'html') - { - $data = json_decode($data, true); - - // History must be loaded on the fly - $filenames = glob(WRITEPATH . 'debugbar/debugbar_*'); - $total = count($filenames); - rsort($filenames); - - $files = []; - - $current = static::$request->getGet('debugbar_time'); - $app = config(App::class); - - for ($i = 0; $i < $total; $i++) - { - // Oldest files will be deleted - if ($app->toolbarMaxHistory >= 0 && $i + 1 > $app->toolbarMaxHistory) - { - unlink($filenames[$i]); - continue; - } - - // Get the contents of this specific history request - ob_start(); - include($filenames[$i]); - $contents = ob_get_contents(); - ob_end_clean(); - - $file = json_decode($contents, true); - - // Debugbar files shown in History Collector - $files[] = [ - 'time' => (int)$time = substr($filenames[$i], -10), - 'datetime' => date('Y-m-d H:i:s', $time), - 'active' => (int)($time === $current), - 'status' => $file['vars']['response']['statusCode'], - 'method' => $file['method'], - 'url' => $file['url'], - 'isAJAX' => $file['isAJAX'] ? 'Yes' : 'No', - 'contentType' => $file['vars']['response']['contentType'], - ]; - } - - // Set the History here. Class is not necessary - $data['collectors'][] = [ - 'title' => 'History', - 'titleSafe' => 'history', - 'titleDetails' => '', - 'display' => ['files' => $files], - 'badgeValue' => $count = count($files), - 'isEmpty' => ! (bool)$count, - 'hasTabContent' => true, - 'hasLabel' => true, - 'icon' => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAJySURBVEhL3ZU7aJNhGIVTpV6i4qCIgkIHxcXLErS4FBwUFNwiCKGhuTYJGaIgnRoo4qRu6iCiiIuIXXTTIkIpuqoFwaGgonUQlC5KafU5ycmNP0lTdPLA4fu+8573/a4/f6hXpFKpwUwmc9fDfweKbk+n07fgEv33TLSbtt/hvwNFT1PsG/zdTE0Gp+GFfD6/2fbVIxqNrqPIRbjg4t/hY8aztcngfDabHXbKyiiXy2vcrcPH8oDCry2FKDrA+Ar6L01E/ypyXzXaARjDGGcoeNxSDZXE0dHRA5VRE5LJ5CFy5jzJuOX2wHRHRnjbklZ6isQ3tIctBaAd4vlK3jLtkOVWqABBXd47jGHLmjTmSScttQV5J+SjfcUweFQEbsjAas5aqoCLXutJl7vtQsAzpRowYqkBinyCC8Vicb2lOih8zoldd0F8RD7qTFiqAnGrAy8stUAvi/hbqDM+YzkAFrLPdR5ZqoLXsd+Bh5YCIH7JniVdquUWxOPxDfboHhrI5XJ7HHhiqQXox+APe/Qk64+gGYVCYZs8cMpSFQj9JOoFzVqqo7k4HIvFYpscCoAjOmLffUsNUGRaQUwDlmofUa34ecsdgXdcXo4wbakBgiUFafXJV8A4DJ/2UrxUKm3E95H8RbjLcgOJRGILhnmCP+FBy5XvwN2uIPcy1AJvWgqC4xm2aU4Xb3lF4I+Tpyf8hRe5w3J7YLymSeA8Z3nSclv4WLRyFdfOjzrUFX0klJUEtZtntCNc+F69cz/FiDzEPtjzmcUMOr83kDQEX6pAJxJfpL3OX22n01YN7SZCoQnaSdoZ+Jz+PZihH3wt/xlCoT9M6nEtmRSPCQAAAABJRU5ErkJggg==', - 'hasTimelineData' => false, - 'timelineData' => [], - ]; - - $output = ''; - - switch ($format) - { - case 'html': - $data['styles'] = []; - extract($data); - $parser = Services::parser(BASEPATH . 'Debug/Toolbar/Views/', null, false); - ob_start(); - include(__DIR__ . '/Toolbar/Views/toolbar.tpl.php'); - $output = ob_get_contents(); - ob_end_clean(); - break; - case 'json': - $output = json_encode($data); - break; - case 'xml': - $formatter = new XMLFormatter; - $output = $formatter->format($data); - break; - } - - return $output; - } - //-------------------------------------------------------------------- /** @@ -312,10 +208,10 @@ * * @return string */ - protected static function renderTimeline(array $collectors, $startTime, int $segmentCount, int $segmentDuration, array& $styles): string + protected function renderTimeline(array $collectors, $startTime, int $segmentCount, int $segmentDuration, array& $styles): string { $displayTime = $segmentCount * $segmentDuration; - $rows = static::collectTimelineData($collectors); + $rows = $this->collectTimelineData($collectors); $output = ''; $styleCount = 0; @@ -349,7 +245,7 @@ * * @return array */ - protected static function collectTimelineData($collectors): array + protected function collectTimelineData($collectors): array { $data = []; @@ -413,60 +309,174 @@ //-------------------------------------------------------------------- + public function prepare() + { + if (CI_DEBUG && ! is_cli()) + { + global $app; + + $request = Services::request(); + $response = Services::response(); + + $toolbar = Services::toolbar(config(Toolbar::class)); + $stats = $app->getPerformanceStats(); + $data = $toolbar->run( + $stats['startTime'], + $stats['totalTime'], + $request, + $response + ); + + helper('filesystem'); + + // Updated to time() so we can get history + $time = time(); + + if (! is_dir(WRITEPATH . 'debugbar')) + { + mkdir(WRITEPATH . 'debugbar', 0777); + } + + write_file(WRITEPATH . 'debugbar/' . 'debugbar_' . $time . '.json', $data, 'w+'); + + $format = $response->getHeaderLine('content-type'); + + // Non-HTML formats should not include the debugbar + // then we send headers saying where to find the debug data + // for this response + if ($request->isAJAX() || strpos($format, 'html') === false) + { + $response->setHeader('Debugbar-Time', $time) + ->setHeader('Debugbar-Link', site_url("?debugbar_time={$time}")) + ->getBody(); + + return; + } + + $script = PHP_EOL + . '' + . '' + . '' + . PHP_EOL; + + if (strpos($response->getBody(), '') !== false) + { + $response->setBody( + str_replace('', $script . '', $response->getBody()) + ); + + return; + } + + $response->appendBody($script); + } + } + + //-------------------------------------------------------------------- + /** * */ - public static function eventHandler() + public function respond() { - static::$request = Services::request(); - if (ENVIRONMENT === 'testing') { return; } + $request = Services::request(); + // If the request contains '?debugbar then we're // simply returning the loading script - if (static::$request->getGet('debugbar') !== null) + if ($request->getGet('debugbar') !== null) { // Let the browser know that we are sending javascript header('Content-Type: application/javascript'); ob_start(); - include(BASEPATH . 'Debug/Toolbar/toolbarloader.js.php'); - $output = ob_get_contents(); - @ob_end_clean(); + include($this->config->viewsPath . 'toolbarloader.js.php'); + $output = ob_get_clean(); exit($output); } // Otherwise, if it includes ?debugbar_time, then // we should return the entire debugbar. - if (static::$request->getGet('debugbar_time')) + if ($request->getGet('debugbar_time')) { helper('security'); // Negotiate the content-type to format the output - $format = static::$request->negotiate('media', [ + $format = $request->negotiate('media', [ 'text/html', 'application/json', 'application/xml', ]); $format = explode('/', $format)[1]; - $file = sanitize_filename('debugbar_' . static::$request->getGet('debugbar_time')); - $filename = WRITEPATH . 'debugbar/' . $file; + $file = sanitize_filename('debugbar_' . $request->getGet('debugbar_time')); + $filename = WRITEPATH . 'debugbar/' . $file . '.json'; // Show the toolbar if (is_file($filename)) { - $contents = static::format(file_get_contents($filename), $format); + $contents = $this->format(file_get_contents($filename), $format); exit($contents); } // File was not written or do not exists http_response_code(404); - exit(); // Exit here is needed to avoid load the index page + exit; // Exit here is needed to avoid load the index page } } + + /** + * Format output + * + * @param string $data JSON encoded Toolbar data + * @param string $format html, json, xml + * + * @return string + */ + protected function format(string $data, string $format = 'html') + { + $data = json_decode($data, true); + + if ($this->config->maxHistory !== 0) + { + $history = new History(); + $history->setFiles( + Services::request()->getGet('debugbar_time'), + $this->config->maxHistory + ); + + $data['collectors'][] = $history->getAsArray(); + } + + $output = ''; + + switch ($format) + { + case 'html': + $data['styles'] = []; + extract($data); + $parser = Services::parser($this->config->viewsPath, null, false); + ob_start(); + include($this->config->viewsPath . 'toolbar.tpl.php'); + $output = ob_get_clean(); + break; + case 'json': + $formatter = new JSONFormatter(); + $output = $formatter->format($data); + break; + case 'xml': + $formatter = new XMLFormatter; + $output = $formatter->format($data); + break; + } + + return $output; + } } diff --git a/system/Debug/Toolbar/Collectors/BaseCollector.php b/system/Debug/Toolbar/Collectors/BaseCollector.php index a203599..3a5889d 100644 --- a/system/Debug/Toolbar/Collectors/BaseCollector.php +++ b/system/Debug/Toolbar/Collectors/BaseCollector.php @@ -255,9 +255,9 @@ { $file = 'APPPATH/' . substr($file, strlen(APPPATH)); } - elseif (strpos($file, BASEPATH) === 0) + elseif (strpos($file, SYSTEMPATH) === 0) { - $file = 'BASEPATH/' . substr($file, strlen(BASEPATH)); + $file = 'SYSTEMPATH/' . substr($file, strlen(SYSTEMPATH)); } elseif (strpos($file, FCPATH) === 0) { @@ -302,4 +302,21 @@ return ''; } + public function getAsArray() + { + return [ + 'title' => $this->getTitle(), + 'titleSafe' => $this->getTitle(true), + 'titleDetails' => $this->getTitleDetails(), + 'display' => $this->display(), + 'badgeValue' => $this->getBadgeValue(), + 'isEmpty' => $this->isEmpty(), + 'hasTabContent' => $this->hasTabContent(), + 'hasLabel' => $this->hasLabel(), + 'icon' => $this->icon(), + 'hasTimelineData' => $this->hasTimelineData(), + 'timelineData' => $this->timelineData(), + ]; + } + } diff --git a/system/Debug/Toolbar/Collectors/Files.php b/system/Debug/Toolbar/Collectors/Files.php index 21f017b..5ecf23c 100644 --- a/system/Debug/Toolbar/Collectors/Files.php +++ b/system/Debug/Toolbar/Collectors/Files.php @@ -95,7 +95,7 @@ { $path = $this->cleanPath($file); - if (strpos($path, 'BASEPATH') !== false) + if (strpos($path, 'SYSTEMPATH') !== false) { $coreFiles[] = [ 'name' => basename($file), diff --git a/system/Debug/Toolbar/Collectors/History.php b/system/Debug/Toolbar/Collectors/History.php new file mode 100644 index 0000000..8be0f2a --- /dev/null +++ b/system/Debug/Toolbar/Collectors/History.php @@ -0,0 +1,170 @@ += 0 && $counter > $limit) + { + unlink($filename); + continue; + } + + // Get the contents of this specific history request + $contents = file_get_contents($filename); + $contents = json_decode($contents); + + \preg_match_all('/\d+/', $filename, $time); + $time = (int)$time[0][0]; + + // Debugbar files shown in History Collector + $files[] = [ + 'time' => $time, + 'datetime' => date('Y-m-d H:i:s', $time), + 'active' => $time === $current, + 'status' => $contents->vars->response->statusCode, + 'method' => $contents->method, + 'url' => $contents->url, + 'isAJAX' => $contents->isAJAX ? 'Yes' : 'No', + 'contentType' => $contents->vars->response->contentType, + ]; + } + + $this->files = $files; + } + + //-------------------------------------------------------------------- + + /** + * Returns the data of this collector to be formatted in the toolbar + * + * @return array + */ + public function display(): array + { + return ['files' => $this->files]; + } + + //-------------------------------------------------------------------- + + /** + * Displays the number of included files as a badge in the tab button. + * + * @return integer + */ + public function getBadgeValue() + { + return count($this->files); + } + + public function isEmpty() + { + return empty($this->files); + } + + //-------------------------------------------------------------------- + + /** + * Display the icon. + * + * Icon from https://icons8.com - 1em package + * + * @return string + */ + public function icon(): string + { + return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAJySURBVEhL3ZU7aJNhGIVTpV6i4qCIgkIHxcXLErS4FBwUFNwiCKGhuTYJGaIgnRoo4qRu6iCiiIuIXXTTIkIpuqoFwaGgonUQlC5KafU5ycmNP0lTdPLA4fu+8573/a4/f6hXpFKpwUwmc9fDfweKbk+n07fgEv33TLSbtt/hvwNFT1PsG/zdTE0Gp+GFfD6/2fbVIxqNrqPIRbjg4t/hY8aztcngfDabHXbKyiiXy2vcrcPH8oDCry2FKDrA+Ar6L01E/ypyXzXaARjDGGcoeNxSDZXE0dHRA5VRE5LJ5CFy5jzJuOX2wHRHRnjbklZ6isQ3tIctBaAd4vlK3jLtkOVWqABBXd47jGHLmjTmSScttQV5J+SjfcUweFQEbsjAas5aqoCLXutJl7vtQsAzpRowYqkBinyCC8Vicb2lOih8zoldd0F8RD7qTFiqAnGrAy8stUAvi/hbqDM+YzkAFrLPdR5ZqoLXsd+Bh5YCIH7JniVdquUWxOPxDfboHhrI5XJ7HHhiqQXox+APe/Qk64+gGYVCYZs8cMpSFQj9JOoFzVqqo7k4HIvFYpscCoAjOmLffUsNUGRaQUwDlmofUa34ecsdgXdcXo4wbakBgiUFafXJV8A4DJ/2UrxUKm3E95H8RbjLcgOJRGILhnmCP+FBy5XvwN2uIPcy1AJvWgqC4xm2aU4Xb3lF4I+Tpyf8hRe5w3J7YLymSeA8Z3nSclv4WLRyFdfOjzrUFX0klJUEtZtntCNc+F69cz/FiDzEPtjzmcUMOr83kDQEX6pAJxJfpL3OX22n01YN7SZCoQnaSdoZ+Jz+PZihH3wt/xlCoT9M6nEtmRSPCQAAAABJRU5ErkJggg=='; + } +} diff --git a/system/Debug/Toolbar/Views/toolbar.tpl.php b/system/Debug/Toolbar/Views/toolbar.tpl.php index d6b387a..2494a83 100644 --- a/system/Debug/Toolbar/Views/toolbar.tpl.php +++ b/system/Debug/Toolbar/Views/toolbar.tpl.php @@ -1,3 +1,24 @@ + @@ -11,7 +32,7 @@ width="155.000000px" height="200.000000px" viewBox="0 0 155.000000 200.000000" preserveAspectRatio="xMidYMid meet"> - - + @@ -60,7 +81,7 @@

- + @@ -92,17 +113,18 @@
- - - - - - - - + + + + + + + + - + renderTimeline($collectors, $startTime, $segmentCount, $segmentDuration, + $styles) ?>
NAMECOMPONENTDURATION ms
NAMECOMPONENTDURATION ms
@@ -110,7 +132,7 @@ - +

@@ -124,10 +146,11 @@
- + $items) : ?> - +

@@ -218,10 +241,10 @@ $value) : ?> - - - - + + + +
@@ -237,14 +260,16 @@ $value) : ?> - + -

Response ( )

+

Response + ( ) +

@@ -276,5 +301,6 @@ . { } + diff --git a/system/Debug/Toolbar/Views/toolbarloader.js.php b/system/Debug/Toolbar/Views/toolbarloader.js.php new file mode 100644 index 0000000..62e3418 --- /dev/null +++ b/system/Debug/Toolbar/Views/toolbarloader.js.php @@ -0,0 +1,84 @@ + +document.addEventListener('DOMContentLoaded', loadDoc, false); + +function loadDoc(time) { + if (isNaN(time)) { + time = document.getElementById("debugbar_loader").getAttribute("data-time"); + localStorage.setItem('debugbar-time', time); + } + + localStorage.setItem('debugbar-time-new', time); + + var url = ""; + + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState === 4 && this.status === 200) { + var toolbar = document.getElementById("toolbarContainer"); + if (!toolbar) { + toolbar = document.createElement('div'); + toolbar.setAttribute('id', 'toolbarContainer'); + document.body.appendChild(toolbar); + } + + // copy for easier manipulation + let responseText = this.responseText; + + // get csp blocked parts + // the style block is the first and starts at 0 + { + let PosBeg = responseText.indexOf( '>', responseText.indexOf( '', PosBeg ); + document.getElementById( 'debugbar_dynamic_style' ).innerHTML = responseText.substr( PosBeg, PosEnd - PosBeg ); + responseText = responseText.substr( PosEnd + 8 ); + } + // the script block starts right after style blocks ended + { + let PosBeg = responseText.indexOf( '>', responseText.indexOf( '' ); + document.getElementById( 'debugbar_dynamic_script' ).innerHTML = responseText.substr( PosBeg, PosEnd - PosBeg ); + responseText = responseText.substr( PosEnd + 9 ); + } + // check for last style block + { + let PosBeg = responseText.indexOf( '>', responseText.lastIndexOf( '', PosBeg ); + document.getElementById( 'debugbar_dynamic_style' ).innerHTML += responseText.substr( PosBeg, PosEnd - PosBeg ); + responseText = responseText.substr( 0, PosBeg + 8 ); + } + + toolbar.innerHTML = responseText; + if (typeof ciDebugBar === 'object') { + ciDebugBar.init(); + } + } else if (this.readyState === 4 && this.status === 404) { + console.log('CodeIgniter DebugBar: File "WRITEPATH/debugbar/debugbar_' + time + '" not found.'); + } + }; + + xhttp.open("GET", url + "?debugbar_time=" + time, true); + xhttp.send(); +} + +// Track all AJAX requests +var oldXHR = window.XMLHttpRequest; + +function newXHR() { + var realXHR = new oldXHR(); + realXHR.addEventListener("readystatechange", function() { + // Only success responses and URLs that do not contains "debugbar_time" are tracked + if (realXHR.readyState === 4 && realXHR.status.toString()[0] === '2' && realXHR.responseURL.indexOf('debugbar_time') === -1) { + var debugbarTime = realXHR.getResponseHeader('Debugbar-Time'); + if (debugbarTime) { + var h2 = document.querySelector('#ci-history > h2'); + h2.innerHTML = 'History You have new debug data. '; + var badge = document.querySelector('a[data-tab="ci-history"] > span > .badge'); + badge.className += ' active'; + } + } + }, false); + return realXHR; +} + +window.XMLHttpRequest = newXHR; + diff --git a/system/Debug/Toolbar/toolbarloader.js.php b/system/Debug/Toolbar/toolbarloader.js.php deleted file mode 100644 index cb60f10..0000000 --- a/system/Debug/Toolbar/toolbarloader.js.php +++ /dev/null @@ -1,84 +0,0 @@ - -document.addEventListener('DOMContentLoaded', loadDoc, false); - -function loadDoc(time) { - if (isNaN(time)) { - time = document.getElementById("debugbar_loader").getAttribute("data-time"); - localStorage.setItem('debugbar-time', time); - } - - localStorage.setItem('debugbar-time-new', time); - - var url = ""; - - var xhttp = new XMLHttpRequest(); - xhttp.onreadystatechange = function() { - if (this.readyState === 4 && this.status === 200) { - var toolbar = document.getElementById("toolbarContainer"); - if (!toolbar) { - toolbar = document.createElement('div'); - toolbar.setAttribute('id', 'toolbarContainer'); - document.body.appendChild(toolbar); - } - - // copy for easier manipulation - let responseText = this.responseText; - - // get csp blocked parts - // the style block is the first and starts at 0 - { - let PosBeg = responseText.indexOf( '>', responseText.indexOf( '', PosBeg ); - document.getElementById( 'debugbar_dynamic_style' ).innerHTML = responseText.substr( PosBeg, PosEnd ) - responseText = responseText.substr( PosEnd + 8 ); - } - // the script block starts right after style blocks ended - { - let PosBeg = responseText.indexOf( '>', responseText.indexOf( '' ); - document.getElementById( 'debugbar_dynamic_script' ).innerHTML = responseText.substr( PosBeg, PosEnd - PosBeg ); - responseText = responseText.substr( PosEnd + 9 ); - } - // check for last style block - { - let PosBeg = responseText.indexOf( '>', responseText.lastIndexOf( '', PosBeg ); - document.getElementById( 'debugbar_dynamic_style' ).innerHTML += responseText.substr( PosBeg, PosEnd - PosBeg ); - responseText = responseText.substr( 0, PosBeg ); - } - - toolbar.innerHTML = responseText; - if (typeof ciDebugBar === 'object') { - ciDebugBar.init(); - } - } else if (this.readyState === 4 && this.status === 404) { - console.log('CodeIgniter DebugBar: File "WRITEPATH/debugbar/debugbar_' + time + '" not found.'); - } - }; - - xhttp.open("GET", url + "?debugbar_time=" + time, true); - xhttp.send(); -} - -// Track all AJAX requests -var oldXHR = window.XMLHttpRequest; - -function newXHR() { - var realXHR = new oldXHR(); - realXHR.addEventListener("readystatechange", function() { - // Only success responses and URLs that do not contains "debugbar_time" are tracked - if (realXHR.readyState === 4 && realXHR.status.toString()[0] === '2' && realXHR.responseURL.indexOf('debugbar_time') === -1) { - var debugbarTime = realXHR.getResponseHeader('Debugbar-Time'); - if (debugbarTime) { - var h2 = document.querySelector('#ci-history > h2'); - h2.innerHTML = 'History You have new debug data. '; - var badge = document.querySelector('a[data-tab="ci-history"] > span > .badge'); - badge.className += ' active'; - } - } - }, false); - return realXHR; -} - -window.XMLHttpRequest = newXHR; - diff --git a/system/Entity.php b/system/Entity.php index 58bab62..5068762 100644 --- a/system/Entity.php +++ b/system/Entity.php @@ -117,11 +117,15 @@ * that may or may not exist. * * @param array $data + * + * @return \CodeIgniter\Entity */ public function fill(array $data) { foreach ($data as $key => $value) { + $key = $this->mapProperty($key); + $method = 'set' . str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $key))); if (method_exists($this, $method)) @@ -133,6 +137,8 @@ $this->$key = $value; } } + + return $this; } //-------------------------------------------------------------------- @@ -412,6 +418,7 @@ /** * Provides the ability to cast an item as a specific data type. + * Add ? at the beginning of $type (i.e. ?string) to get NULL instead of castig $value if $value === null * * @param $value * @param string $type @@ -421,9 +428,12 @@ protected function castAs($value, string $type) { + if(substr($type,0,1) === '?' && $value === null) return null; + switch($type) { - case 'integer': + case 'int': + case 'integer': //alias for 'integer' $value = (int)$value; break; case 'float': @@ -435,7 +445,8 @@ case 'string': $value = (string)$value; break; - case 'boolean': + case 'bool': + case 'boolean': //alias for 'boolean' $value = (bool)$value; break; case 'object': diff --git a/system/HTTP/ContentSecurityPolicy.php b/system/HTTP/ContentSecurityPolicy.php index 20f6c0f..cf94f2f 100644 --- a/system/HTTP/ContentSecurityPolicy.php +++ b/system/HTTP/ContentSecurityPolicy.php @@ -1,4 +1,5 @@ - element. * * @see http://www.w3.org/TR/CSP/#directive-base-uri * - * @param string $uri - * @param boolean $reportOnly + * @param string|array $uri + * @param boolean|null $explicitReporting * * @return $this */ - public function setBaseURI($uri, bool $reportOnly) + public function addBaseURI($uri, ?bool $explicitReporting = null) { - $this->baseURI = [(string) $uri => $reportOnly]; + $this->addOption($uri, 'baseURI', $explicitReporting ?? $this->reportOnly); return $this; } @@ -304,14 +302,14 @@ * * @see http://www.w3.org/TR/CSP/#directive-child-src * - * @param $uri - * @param boolean $reportOnly + * @param string|array $uri + * @param boolean|null $explicitReporting * * @return $this */ - public function addChildSrc($uri, bool $reportOnly = false) + public function addChildSrc($uri, ?bool $explicitReporting = null) { - $this->addOption($uri, 'childSrc', $reportOnly); + $this->addOption($uri, 'childSrc', $explicitReporting ?? $this->reportOnly); return $this; } @@ -327,14 +325,14 @@ * * @see http://www.w3.org/TR/CSP/#directive-connect-src * - * @param $uri - * @param boolean $reportOnly + * @param string|array $uri + * @param boolean|null $explicitReporting * * @return $this */ - public function addConnectSrc($uri, bool $reportOnly = false) + public function addConnectSrc($uri, ?bool $explicitReporting = null) { - $this->addOption($uri, 'connectSrc', $reportOnly); + $this->addOption($uri, 'connectSrc', $explicitReporting ?? $this->reportOnly); return $this; } @@ -350,14 +348,14 @@ * * @see http://www.w3.org/TR/CSP/#directive-default-src * - * @param $uri - * @param boolean $reportOnly + * @param string|array $uri + * @param boolean|null $explicitReporting * * @return $this */ - public function setDefaultSrc($uri, bool $reportOnly = false) + public function setDefaultSrc($uri, ?bool $explicitReporting = null) { - $this->defaultSrc = [(string) $uri => $reportOnly]; + $this->defaultSrc = [(string) $uri => $explicitReporting ?? $this->reportOnly]; return $this; } @@ -372,14 +370,14 @@ * * @see http://www.w3.org/TR/CSP/#directive-font-src * - * @param $uri - * @param boolean $reportOnly + * @param string|array $uri + * @param boolean|null $explicitReporting * * @return $this */ - public function addFontSrc($uri, bool $reportOnly = false) + public function addFontSrc($uri, ?bool $explicitReporting = null) { - $this->addOption($uri, 'fontSrc', $reportOnly); + $this->addOption($uri, 'fontSrc', $explicitReporting ?? $this->reportOnly); return $this; } @@ -392,14 +390,14 @@ * * @see http://www.w3.org/TR/CSP/#directive-form-action * - * @param $uri - * @param boolean $reportOnly + * @param string|array $uri + * @param boolean|null $explicitReporting * * @return $this */ - public function addFormAction($uri, bool $reportOnly = false) + public function addFormAction($uri, ?bool $explicitReporting = null) { - $this->addOption($uri, 'formAction', $reportOnly); + $this->addOption($uri, 'formAction', $explicitReporting ?? $this->reportOnly); return $this; } @@ -412,14 +410,14 @@ * * @see http://www.w3.org/TR/CSP/#directive-frame-ancestors * - * @param $uri - * @param boolean $reportOnly + * @param string|array $uri + * @param boolean|null $explicitReporting * * @return $this */ - public function addFrameAncestor($uri, bool $reportOnly = false) + public function addFrameAncestor($uri, ?bool $explicitReporting = null) { - $this->addOption($uri, 'frameAncestors', $reportOnly); + $this->addOption($uri, 'frameAncestors', $explicitReporting ?? $this->reportOnly); return $this; } @@ -432,14 +430,14 @@ * * @see http://www.w3.org/TR/CSP/#directive-img-src * - * @param $uri - * @param boolean $reportOnly + * @param string|array $uri + * @param boolean|null $explicitReporting * * @return $this */ - public function addImageSrc($uri, bool $reportOnly = false) + public function addImageSrc($uri, ?bool $explicitReporting = null) { - $this->addOption($uri, 'imageSrc', $reportOnly); + $this->addOption($uri, 'imageSrc', $explicitReporting ?? $this->reportOnly); return $this; } @@ -452,14 +450,14 @@ * * @see http://www.w3.org/TR/CSP/#directive-media-src * - * @param $uri - * @param boolean $reportOnly + * @param string|array $uri + * @param boolean|null $explicitReporting * * @return $this */ - public function addMediaSrc($uri, bool $reportOnly = false) + public function addMediaSrc($uri, ?bool $explicitReporting = null) { - $this->addOption($uri, 'mediaSrc', $reportOnly); + $this->addOption($uri, 'mediaSrc', $explicitReporting ?? $this->reportOnly); return $this; } @@ -472,14 +470,14 @@ * * @see https://www.w3.org/TR/CSP/#directive-manifest-src * - * @param $uri - * @param boolean $reportOnly + * @param string|array $uri + * @param boolean|null $explicitReporting * * @return $this */ - public function addManifestSrc($uri, bool $reportOnly = false) + public function addManifestSrc($uri, ?bool $explicitReporting = null) { - $this->addOption($uri, 'manifestSrc', $reportOnly); + $this->addOption($uri, 'manifestSrc', $explicitReporting ?? $this->reportOnly); return $this; } @@ -492,14 +490,14 @@ * * @see http://www.w3.org/TR/CSP/#directive-object-src * - * @param $uri - * @param boolean $reportOnly + * @param string|array $uri + * @param boolean|null $explicitReporting * * @return $this */ - public function addObjectSrc($uri, bool $reportOnly = false) + public function addObjectSrc($uri, ?bool $explicitReporting = null) { - $this->addOption($uri, 'objectSrc', $reportOnly); + $this->addOption($uri, 'objectSrc', $explicitReporting ?? $this->reportOnly); return $this; } @@ -512,14 +510,14 @@ * * @see http://www.w3.org/TR/CSP/#directive-plugin-types * - * @param string $mime One or more plugin mime types, separate by spaces - * @param boolean $reportOnly + * @param string|array $mime One or more plugin mime types, separate by spaces + * @param boolean|null $explicitReporting * * @return $this */ - public function addPluginType($mime, bool $reportOnly = false) + public function addPluginType($mime, ?bool $explicitReporting = null) { - $this->addOption($mime, 'pluginTypes', $reportOnly); + $this->addOption($mime, 'pluginTypes', $explicitReporting ?? $this->reportOnly); return $this; } @@ -532,7 +530,7 @@ * * @see http://www.w3.org/TR/CSP/#directive-report-uri * - * @param $uri + * @param string $uri * * @return $this */ @@ -551,22 +549,14 @@ * * @see http://www.w3.org/TR/CSP/#directive-sandbox * - * @param boolean $value - * @param array $flags An array of sandbox flags that can be added to the directive. + * @param string|array $flags An array of sandbox flags that can be added to the directive. + * @param boolean|null $explicitReporting * * @return $this */ - public function setSandbox(bool $value = true, array $flags = null) + public function addSandbox($flags, ?bool $explicitReporting = null) { - if (empty($this->sandbox) && empty($flags)) - { - $this->sandbox = $value; - } - else - { - $this->sandbox = $flags; - } - + $this->addOption($flags, 'sandbox', $explicitReporting ?? $this->reportOnly); return $this; } @@ -578,14 +568,14 @@ * * @see http://www.w3.org/TR/CSP/#directive-connect-src * - * @param $uri - * @param boolean $reportOnly + * @param string|array $uri + * @param boolean|null $explicitReporting * * @return $this */ - public function addScriptSrc($uri, bool $reportOnly = false) + public function addScriptSrc($uri, ?bool $explicitReporting = null) { - $this->addOption($uri, 'scriptSrc', $reportOnly); + $this->addOption($uri, 'scriptSrc', $explicitReporting ?? $this->reportOnly); return $this; } @@ -598,14 +588,14 @@ * * @see http://www.w3.org/TR/CSP/#directive-connect-src * - * @param $uri - * @param boolean $reportOnly + * @param string|array $uri + * @param boolean|null $explicitReporting * * @return $this */ - public function addStyleSrc($uri, bool $reportOnly = false) + public function addStyleSrc($uri, ?bool $explicitReporting = null) { - $this->addOption($uri, 'styleSrc', $reportOnly); + $this->addOption($uri, 'styleSrc', $explicitReporting ?? $this->reportOnly); return $this; } @@ -616,7 +606,7 @@ * Sets whether the user agents should rewrite URL schemes, changing * HTTP to HTTPS. * - * @param boolean|true $value + * @param boolean $value * * @return $this */ @@ -628,18 +618,17 @@ } //-------------------------------------------------------------------- - //-------------------------------------------------------------------- // Utility //-------------------------------------------------------------------- /** * DRY method to add an string or array to a class property. * - * @param $options - * @param string $target - * @param boolean $reportOnly If TRUE, this item will be reported, not restricted + * @param string|array $options + * @param string $target + * @param boolean|null $explicitReporting */ - protected function addOption($options, string $target, bool $reportOnly = false) + protected function addOption($options, string $target, ?bool $explicitReporting = null) { // Ensure we have an array to work with... if (is_string($this->{$target})) @@ -649,18 +638,14 @@ if (is_array($options)) { - $newOptions = []; foreach ($options as $opt) { - $newOptions[] = [$opt => $reportOnly]; + $this->{$target}[$opt] = $explicitReporting ?? $this->reportOnly; } - - $this->{$target} = array_merge($this->{$target}, $newOptions); - unset($newOptions); } else { - $this->{$target}[$options] = $reportOnly; + $this->{$target}[$options] = $explicitReporting ?? $this->reportOnly; } } @@ -750,6 +735,16 @@ 'report-uri' => 'reportURI', ]; + // inject default base & default URIs if needed + if (empty($this->baseURI)) + { + $this->baseURI = 'self'; + } + if (empty($this->defaultSrc)) + { + $this->defaultSrc = 'self'; + } + foreach ($directives as $name => $property) { // base_uri @@ -769,6 +764,12 @@ { $header .= " {$name} {$value};"; } + // add token only if needed + if ($this->upgradeInsecureRequests) + { + $header .= ' upgrade-insecure-requests;'; + } + $response->appendHeader('Content-Security-Policy', $header); } @@ -800,8 +801,6 @@ { if (empty($values)) { - // It's possible that directives like 'sandbox' will not - // have any values passed in, so add them to the main policy. $this->tempHeaders[$name] = null; return; } diff --git a/system/HTTP/DownloadResponse.php b/system/HTTP/DownloadResponse.php index 3b85291..6d72601 100644 --- a/system/HTTP/DownloadResponse.php +++ b/system/HTTP/DownloadResponse.php @@ -399,7 +399,7 @@ // Per spec, MUST be sent with each request, if possible. // http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html - if (isset($this->headers['Date'])) + if (! isset($this->headers['Date'])) { $this->setDate(\DateTime::createFromFormat('U', time())); } diff --git a/system/Helpers/form_helper.php b/system/Helpers/form_helper.php index da7edab..7c2bb0a 100644 --- a/system/Helpers/form_helper.php +++ b/system/Helpers/form_helper.php @@ -927,15 +927,22 @@ foreach ($default as $key => $val) { - if ($key === 'value') + if(!is_bool($val)) { - $val = esc($val, 'html'); + if ($key === 'value') + { + $val = esc($val, 'html'); + } + elseif ($key === 'name' && ! strlen($default['name'])) + { + continue; + } + $att .= $key . '="' . $val . '" '; } - elseif ($key === 'name' && ! strlen($default['name'])) + else { - continue; + $att .= $key . ' '; } - $att .= $key . '="' . $val . '" '; } return $att; diff --git a/system/Helpers/url_helper.php b/system/Helpers/url_helper.php index a7ac752..bbea0aa 100644 --- a/system/Helpers/url_helper.php +++ b/system/Helpers/url_helper.php @@ -55,21 +55,21 @@ } // use alternate config if provided, else default one - $config = empty($altConfig) ? config(\Config\App::class) : $altConfig; + $config = $altConfig ?? config(\Config\App::class); - $base = base_url(); + $fullPath = rtrim(base_url(), '/') . '/'; // Add index page, if so configured if (! empty($config->indexPage)) { - $path = rtrim($base, '/') . '/' . rtrim($config->indexPage, '/') . '/' . $path; + $fullPath .= rtrim($config->indexPage, '/'); } - else + if (! empty($path)) { - $path = rtrim($base, '/') . '/' . $path; + $fullPath .= '/' . $path; } - $url = new \CodeIgniter\HTTP\URI($path); + $url = new \CodeIgniter\HTTP\URI($fullPath); // allow the scheme to be over-ridden; else, use default if (! empty($scheme)) @@ -145,7 +145,7 @@ */ function current_url(bool $returnObject = false) { - return $returnObject === true ? \CodeIgniter\Config\Services::request()->uri : (string) \CodeIgniter\Config\Services::request()->uri; + return $returnObject ? \CodeIgniter\Config\Services::request()->uri : (string) \CodeIgniter\Config\Services::request()->uri; } } @@ -170,7 +170,7 @@ // Otherwise, grab a sanitized version from $_SERVER. $referer = $_SESSION['_ci_previous_url'] ?? \CodeIgniter\Config\Services::request()->getServer('HTTP_REFERER', FILTER_SANITIZE_URL); - $referer = empty($referer) ? site_url('/') : $referer; + $referer = $referer ?? site_url('/'); return $returnObject ? new \CodeIgniter\HTTP\URI($referer) : $referer; } @@ -208,7 +208,7 @@ function index_page(\Config\App $altConfig = null): string { // use alternate config if provided, else default one - $config = empty($altConfig) ? config(\Config\App::class) : $altConfig; + $config = $altConfig ?? config(\Config\App::class); return $config->indexPage; } @@ -233,7 +233,7 @@ function anchor($uri = '', string $title = '', $attributes = '', \Config\App $altConfig = null): string { // use alternate config if provided, else default one - $config = empty($altConfig) ? config(\Config\App::class) : $altConfig; + $config = $altConfig ?? config(\Config\App::class); $site_url = is_array($uri) ? site_url($uri, null, $config) : (preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri, null, $config)); // eliminate trailing slash @@ -273,7 +273,7 @@ function anchor_popup($uri = '', string $title = '', $attributes = false, \Config\App $altConfig = null): string { // use alternate config if provided, else default one - $config = empty($altConfig) ? config(\Config\App::class) : $altConfig; + $config = $altConfig ?? config(\Config\App::class); $site_url = preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri, '', $config); $site_url = rtrim($site_url, '/'); diff --git a/system/I18n/Time.php b/system/I18n/Time.php index eee2da4..36b40cf 100644 --- a/system/I18n/Time.php +++ b/system/I18n/Time.php @@ -1,4 +1,5 @@ -getTimestamp(); $time = $this->getTimestamp(); - if (! $now >= $time) - { - return 0; - } - - return date('Y', $now) - date('Y', $time); + // future dates have no age + return max(0, date('Y', $now) - date('Y', $time)); } //-------------------------------------------------------------------- @@ -543,20 +540,20 @@ */ public function getDst() { - $start = strtotime('-1 year', $this->getTimestamp()); - $end = strtotime('+2 year', $start); - + // grab the transactions that would affect today + $start = strtotime('-1 year', $this->getTimestamp()); + $end = strtotime('+2 year', $start); $transitions = $this->timezone->getTransitions($start, $end); + $daylightSaving = false; foreach ($transitions as $transition) { if ($transition['time'] > $this->format('U')) { - return (bool) $transition['isdst']; + $daylightSaving = (bool) $transition['isdst'] ?? $daylightSaving; } } - - return false; + return $daylightSaving; } //-------------------------------------------------------------------- @@ -644,11 +641,11 @@ throw I18nException::forInvalidDay($value); } - $date = $this->getYear() . '-' . $this->getMonth(); - $lastDay = date('t', strtotime($date)); + $date = $this->getYear() . '-' . $this->getMonth(); + $lastDay = date('t', strtotime($date)); if ($value > $lastDay) - { - throw I18nException::forInvalidOverDay($lastDay, $value); + { + throw I18nException::forInvalidOverDay($lastDay, $value); } return $this->setValue('day', $value); @@ -980,15 +977,14 @@ * * @return string */ - public function toLocalizedString(string $format = null) + public function toLocalizedString(?string $format = null) { - $format = is_null($format) ? $this->toStringFormat : $format; + $format = $format ?? $this->toStringFormat; return IntlDateFormatter::formatObject($this->toDateTime(), $format, $this->locale); } //-------------------------------------------------------------------- - //-------------------------------------------------------------------- // Comparison //-------------------------------------------------------------------- @@ -1009,8 +1005,8 @@ $testTime = $this->getUTCObject($testTime, $timezone); $ourTime = $this->toDateTime() - ->setTimezone(new DateTimeZone('UTC')) - ->format('Y-m-d H:i:s'); + ->setTimezone(new DateTimeZone('UTC')) + ->format('Y-m-d H:i:s'); return $testTime->format('Y-m-d H:i:s') === $ourTime; } @@ -1083,7 +1079,6 @@ } //-------------------------------------------------------------------- - //-------------------------------------------------------------------- // Differences //-------------------------------------------------------------------- @@ -1132,9 +1127,7 @@ // Yesterday/Tommorrow special cases if (abs($days) === 1) { - return $before - ? lang('Time.yesterday') - : lang('Time.tomorrow'); + return $before ? lang('Time.yesterday') : lang('Time.tomorrow'); } $phrase = lang('Time.days', [abs($days)]); @@ -1154,9 +1147,7 @@ return lang('Time.now'); } - return $before - ? lang('Time.ago', [$phrase]) - : lang('Time.inFuture', [$phrase]); + return $before ? lang('Time.ago', [$phrase]) : lang('Time.inFuture', [$phrase]); } /** @@ -1190,7 +1181,7 @@ if ($time instanceof Time) { $time = $time->toDateTime() - ->setTimezone(new DateTimeZone('UTC')); + ->setTimezone(new DateTimeZone('UTC')); } else if ($time instanceof \DateTime) { @@ -1260,6 +1251,11 @@ /** * Allow for property-type access to any getX method... * + * Note that we cannot use this for any of our setX methods, + * as they return new Time objects, but the __set ignores + * return values. + * See http://php.net/manual/en/language.oop5.overloading.php + * * @param $name * * @return mixed @@ -1274,16 +1270,4 @@ } } - //-------------------------------------------------------------------- - - public function __set($name, $value) - { - $method = 'set' . ucfirst($name); - - if (method_exists($this, $method)) - { - return $this->$method($value); - } - } - } diff --git a/system/Language/Language.php b/system/Language/Language.php index 667182f..8f6fe9a 100644 --- a/system/Language/Language.php +++ b/system/Language/Language.php @@ -1,4 +1,5 @@ -locale; + } + + //-------------------------------------------------------------------- + /** * Parses the language string for a file, loads the file, if necessary, * getting the line. @@ -113,16 +133,47 @@ */ public function getLine(string $line, array $args = []) { + // ignore requests with no file specified + if (! strpos($line, '.')) + { + return $line; + } + // Parse out the file name and the actual alias. // Will load the language file and strings. - list($file, $parsedLine) = $this->parseLine($line); + [ + $file, + $parsedLine, + ] = $this->parseLine($line, $this->locale); - $output = $this->language[$this->locale][$file][$parsedLine] ?? $line; + $output = $this->language[$this->locale][$file][$parsedLine] ?? null; + + if ($output === null && strpos($this->locale, '-')) + { + [$locale] = explode('-', $this->locale, 2); + + [ + $file, + $parsedLine, + ] = $this->parseLine($line, $locale); + + $output = $this->language[$locale][$file][$parsedLine] ?? null; + } + + // if still not found, try English + if (empty($output)) + { + $this->parseLine($line, 'en'); + $output = $this->language['en'][$file][$parsedLine] ?? null; + } + + $output = $output ?? $line; if (! empty($args)) { $output = $this->formatMessage($output, $args); } + return $output; } @@ -133,10 +184,11 @@ * filename as the first segment (separated by period). * * @param string $line + * @param string $locale * * @return array */ - protected function parseLine(string $line): array + protected function parseLine(string $line, string $locale): array { // If there's no possibility of a filename being in the string // simply return the string, and they can parse the replacement @@ -152,9 +204,9 @@ $file = substr($line, 0, strpos($line, '.')); $line = substr($line, strlen($file) + 1); - if (! isset($this->language[$this->locale][$file]) || ! array_key_exists($line, $this->language[$this->locale][$file])) + if (! isset($this->language[$locale][$file]) || ! array_key_exists($line, $this->language[$locale][$file])) { - $this->load($file, $this->locale); + $this->load($file, $locale); } return [ @@ -240,7 +292,7 @@ $this->loadedFiles[$locale][] = $file; // Merge our string - $this->language[$this->locale][$file] = $lang; + $this->language[$locale][$file] = $lang; } //-------------------------------------------------------------------- @@ -255,22 +307,19 @@ */ protected function requireFile(string $path): array { - $files = service('locator')->search($path); - + $files = Services::locator()->search($path); $strings = []; foreach ($files as $file) { - if (! is_file($file)) - { - continue; - } - // On some OS's we were seeing failures // on this command returning boolean instead // of array during testing, so we've removed // the require_once for now. - $strings[] = require $file; + if (is_file($file)) + { + $strings[] = require $file; + } } if (isset($strings[1])) diff --git a/system/Language/en/CLI.php b/system/Language/en/CLI.php index 40bc703..4f6176e 100644 --- a/system/Language/en/CLI.php +++ b/system/Language/en/CLI.php @@ -15,6 +15,7 @@ */ return [ + 'commandNotFound' => 'Command "{0}" not found.', 'helpUsage' => 'Usage:', 'helpDescription' => 'Description:', 'helpOptions' => 'Options:', diff --git a/system/Language/en/HTTP.php b/system/Language/en/HTTP.php index 33902f1..6319dc4 100644 --- a/system/Language/en/HTTP.php +++ b/system/Language/en/HTTP.php @@ -31,7 +31,7 @@ 'emptySupportedNegotiations' => 'You must provide an array of supported values to all Negotiations.', // RedirectResponse - 'invalidRoute' => '{0, string} is not a valid route.', + 'invalidRoute' => '{0, string} route cannot be found while reverse-routing.', // DownloadResponse 'cannotSetBinary' => 'When setting filepath can not set binary.', diff --git a/system/Log/Handlers/FileHandler.php b/system/Log/Handlers/FileHandler.php index 12ad9ea..f1786b7 100644 --- a/system/Log/Handlers/FileHandler.php +++ b/system/Log/Handlers/FileHandler.php @@ -108,7 +108,7 @@ // Only add protection to php files if ($this->fileExtension === 'php') { - $msg .= "\n\n"; + $msg .= "\n\n"; } } diff --git a/system/Log/Logger.php b/system/Log/Logger.php index aeb0ab0..660ab1e 100644 --- a/system/Log/Logger.php +++ b/system/Log/Logger.php @@ -146,8 +146,8 @@ /** * Constructor. * - * @param type $config - * @param boolean $debug + * @param \Config\Logger $config + * @param boolean $debug * @throws \RuntimeException */ public function __construct($config, bool $debug = CI_DEBUG) @@ -194,11 +194,11 @@ * @param string $message * @param array $context * - * @return null + * @return boolean */ public function emergency($message, array $context = []) { - $this->log('emergency', $message, $context); + return $this->log('emergency', $message, $context); } //-------------------------------------------------------------------- @@ -212,11 +212,11 @@ * @param string $message * @param array $context * - * @return null + * @return boolean */ public function alert($message, array $context = []) { - $this->log('alert', $message, $context); + return $this->log('alert', $message, $context); } //-------------------------------------------------------------------- @@ -229,11 +229,11 @@ * @param string $message * @param array $context * - * @return null + * @return boolean */ public function critical($message, array $context = []) { - $this->log('critical', $message, $context); + return $this->log('critical', $message, $context); } //-------------------------------------------------------------------- @@ -245,11 +245,11 @@ * @param string $message * @param array $context * - * @return null + * @return boolean */ public function error($message, array $context = []) { - $this->log('error', $message, $context); + return $this->log('error', $message, $context); } //-------------------------------------------------------------------- @@ -263,11 +263,11 @@ * @param string $message * @param array $context * - * @return null + * @return boolean */ public function warning($message, array $context = []) { - $this->log('warning', $message, $context); + return $this->log('warning', $message, $context); } //-------------------------------------------------------------------- @@ -278,11 +278,11 @@ * @param string $message * @param array $context * - * @return null + * @return boolean */ public function notice($message, array $context = []) { - $this->log('notice', $message, $context); + return $this->log('notice', $message, $context); } //-------------------------------------------------------------------- @@ -295,11 +295,11 @@ * @param string $message * @param array $context * - * @return null + * @return boolean */ public function info($message, array $context = []) { - $this->log('info', $message, $context); + return $this->log('info', $message, $context); } //-------------------------------------------------------------------- @@ -310,11 +310,11 @@ * @param string $message * @param array $context * - * @return null + * @return boolean */ public function debug($message, array $context = []) { - $this->log('debug', $message, $context); + return $this->log('debug', $message, $context); } //-------------------------------------------------------------------- @@ -508,7 +508,7 @@ //-------------------------------------------------------------------- /** - * Cleans the paths of filenames by replacing APPPATH, BASEPATH, FCPATH + * Cleans the paths of filenames by replacing APPPATH, SYSTEMPATH, FCPATH * with the actual var. i.e. * * /var/www/site/application/Controllers/Home.php @@ -522,7 +522,7 @@ protected function cleanFileNames($file) { $file = str_replace(APPPATH, 'APPPATH/', $file); - $file = str_replace(BASEPATH, 'BASEPATH/', $file); + $file = str_replace(SYSTEMPATH, 'SYSTEMPATH/', $file); $file = str_replace(FCPATH, 'FCPATH/', $file); return $file; diff --git a/system/Model.php b/system/Model.php index 751adcb..3080437 100644 --- a/system/Model.php +++ b/system/Model.php @@ -343,6 +343,8 @@ $this->tempReturnType = $this->returnType; $this->tempUseSoftDeletes = $this->useSoftDeletes; + $this->reset(); + return $row['data']; } @@ -398,7 +400,7 @@ // Some databases, like PostgreSQL, need order // information to consistently return correct results. - if (empty($builder->QBOrderBy)) + if (empty($builder->QBOrderBy) && ! empty($this->primaryKey)) { $builder->orderBy($this->table . '.' . $this->primaryKey, 'asc'); } diff --git a/system/Session/Handlers/DatabaseHandler.php b/system/Session/Handlers/DatabaseHandler.php index 5cda3bc..bae03a1 100644 --- a/system/Session/Handlers/DatabaseHandler.php +++ b/system/Session/Handlers/DatabaseHandler.php @@ -102,7 +102,7 @@ } // Get DB Connection - $this->DBGroup = ! empty($config->sessionDBGroup) ? $config->sessionDBGroup : 'default'; + $this->DBGroup = $config->sessionDBGroup ?? config(Database::class)->defaultGroup; $this->db = Database::connect($this->DBGroup); diff --git a/system/Test/CIDatabaseTestCase.php b/system/Test/CIDatabaseTestCase.php index 0d73956..0e253e0 100644 --- a/system/Test/CIDatabaseTestCase.php +++ b/system/Test/CIDatabaseTestCase.php @@ -151,7 +151,7 @@ * * @throws ConfigException */ - public function setUp() + protected function setUp() { parent::setUp(); diff --git a/system/Test/CIUnitTestCase.php b/system/Test/CIUnitTestCase.php index 60e6f81..3458111 100644 --- a/system/Test/CIUnitTestCase.php +++ b/system/Test/CIUnitTestCase.php @@ -55,15 +55,7 @@ */ protected $app; - /** - * Path to Config folder, relative - * to the system folder. - * - * @var string - */ - protected $configPath = '../application/Config'; - - public function setUp() + protected function setUp() { parent::setUp(); @@ -202,7 +194,7 @@ * where the result is close but not exactly equal to the * expected time, for reasons beyond our control. * - * @param integer $expected + * @param mixed $expected * @param mixed $actual * @param string $message * @param integer $tolerance @@ -236,52 +228,41 @@ * Loads up an instance of CodeIgniter * and gets the environment setup. * - * @return mixed + * @return \CodeIgniter\CodeIgniter */ protected function createApplication() { - $systemPath = realpath(__DIR__ . '/../'); + $paths = new Paths(); - require_once $systemPath . '/' . $this->configPath . '/Paths.php'; - $paths = $this->adjustPaths(new \Config\Paths()); - - $app = require $systemPath . '/bootstrap.php'; - return $app; + return require realpath(__DIR__ . '/../') . '/bootstrap.php'; } + //-------------------------------------------------------------------- /** - * Attempts to adjust our system paths to account - * for relative location of our tests folder. - * Not foolproof, but works well for default locations. + * Return first matching emitted header. * - * @param \Config\Paths $paths + * @param string $header Identifier of the header of interest + * @param bool $ignoreCase * - * @return \Config\Paths + * @return string|null The value of the header found, null if not found */ - protected function adjustPaths(Paths $paths) + // + protected function getHeaderEmitted(string $header, bool $ignoreCase = false): ?string { - $tests = [ - 'systemDirectory', - 'applicationDirectory', - 'writableDirectory', - 'testsDirectory', - ]; + $found = false; - foreach ($tests as $test) + foreach (xdebug_get_headers() as $emitted) { - if (is_dir($paths->$test) || strpos($paths->$test, '../') !== 0) + $found = $ignoreCase ? + (stripos($emitted, $header) === 0) : + (strpos($emitted, $header) === 0); + if ($found) { - continue; - } - - $check = substr($paths->$test, 3); - if (is_dir($check)) - { - $paths->$test = $check; + return $emitted; } } - return $paths; + return null; } } diff --git a/system/Test/FeatureResponse.php b/system/Test/FeatureResponse.php index cf95e6e..e4a31f3 100644 --- a/system/Test/FeatureResponse.php +++ b/system/Test/FeatureResponse.php @@ -298,7 +298,7 @@ /** * Returns the response's body as JSON * - * @return mixed|string + * @return mixed|false */ public function getJSON() { @@ -306,7 +306,7 @@ if (is_null($response)) { - $this->fail('The Response contained invalid JSON.'); + return false; } return $response; diff --git a/system/Test/FeatureTestCase.php b/system/Test/FeatureTestCase.php index 6f83b9e..afdd65b 100644 --- a/system/Test/FeatureTestCase.php +++ b/system/Test/FeatureTestCase.php @@ -37,7 +37,7 @@ /** * Enabled auto clean op buffer after request call * - * @var bool + * @var boolean */ protected $clean = true; @@ -122,10 +122,13 @@ ->run($this->routes, true); // Clean up any open output buffers + // not relevant to unit testing + // @codeCoverageIgnoreStart if (ob_get_level() > 0 && $this->clean) { ob_end_clean(); } + // @codeCoverageIgnoreEnd $featureResponse = new FeatureResponse($response); diff --git a/system/View/View.php b/system/View/View.php index 7df7192..d41e942 100644 --- a/system/View/View.php +++ b/system/View/View.php @@ -215,26 +215,23 @@ if (CI_DEBUG && (! isset($options['debug']) || $options['debug'] === true)) { - $after = (new \Config\Filters())->globals['after']; - if (in_array('toolbar', $after) || array_key_exists('toolbar', $after)) + $toolbarCollectors = config(\Config\Toolbar::class)->collectors; + + if (in_array(\CodeIgniter\Debug\Toolbar\Collectors\Views::class, $toolbarCollectors)) { - $toolbarCollectors = (config(\Config\App::class))->toolbarCollectors; - if (in_array('CodeIgniter\Debug\Toolbar\Collectors\Views', $toolbarCollectors) || array_key_exists('CodeIgniter\Debug\Toolbar\Collectors\Views', $toolbarCollectors)) + // Clean up our path names to make them a little cleaner + foreach (['APPPATH', 'SYSTEMPATH', 'ROOTPATH'] as $path) { - // Clean up our path names to make them a little cleaner - foreach (['APPPATH', 'BASEPATH', 'ROOTPATH'] as $path) + if (strpos($this->renderVars['file'], constant($path)) === 0) { - if (strpos($this->renderVars['file'], constant($path)) === 0) - { - $this->renderVars['file'] = str_replace(constant($path), $path . '/', $this->renderVars['file']); - break; - } + $this->renderVars['file'] = str_replace(constant($path), $path . '/', $this->renderVars['file']); + break; } - $this->renderVars['file'] = ++$this->viewsCount . ' ' . $this->renderVars['file']; - $output = '' . PHP_EOL - . $output . PHP_EOL - . '' . PHP_EOL; } + $this->renderVars['file'] = ++$this->viewsCount . ' ' . $this->renderVars['file']; + $output = '' . PHP_EOL + . $output . PHP_EOL + . '' . PHP_EOL; } } diff --git a/system/bootstrap.php b/system/bootstrap.php index 30a1d33..3e3e491 100644 --- a/system/bootstrap.php +++ b/system/bootstrap.php @@ -46,32 +46,28 @@ * so they are available in the config files that are loaded. */ -$public = trim($paths->publicDirectory, '/'); - -$pos = strrpos(FCPATH, $public . DIRECTORY_SEPARATOR); - -/** - * The path to the main application directory. Just above public. - */ -if (! defined('ROOTPATH')) -{ - define('ROOTPATH', substr_replace(FCPATH, '', $pos, strlen($public . DIRECTORY_SEPARATOR))); -} - /** * The path to the application directory. */ if (! defined('APPPATH')) { - define('APPPATH', realpath(ROOTPATH . $paths->applicationDirectory) . DIRECTORY_SEPARATOR); + define('APPPATH', realpath($paths->appDirectory) . DIRECTORY_SEPARATOR); +} + +/** + * The path to the project root directory. Just above APPPATH. + */ +if (! defined('ROOTPATH')) +{ + define('ROOTPATH', realpath(APPPATH . '../') . DIRECTORY_SEPARATOR); } /** * The path to the system directory. */ -if (! defined('BASEPATH')) +if (! defined('SYSTEMPATH')) { - define('BASEPATH', realpath(ROOTPATH . $paths->systemDirectory) . DIRECTORY_SEPARATOR); + define('SYSTEMPATH', realpath($paths->systemDirectory) . DIRECTORY_SEPARATOR); } /** @@ -79,7 +75,7 @@ */ if (! defined('WRITEPATH')) { - define('WRITEPATH', realpath(ROOTPATH . $paths->writableDirectory) . DIRECTORY_SEPARATOR); + define('WRITEPATH', realpath($paths->writableDirectory) . DIRECTORY_SEPARATOR); } /** @@ -87,7 +83,7 @@ */ if (! defined('TESTPATH')) { - define('TESTPATH', realpath(ROOTPATH . $paths->testsDirectory) . DIRECTORY_SEPARATOR); + define('TESTPATH', realpath($paths->testsDirectory) . DIRECTORY_SEPARATOR); } /* @@ -97,7 +93,7 @@ */ require_once APPPATH . 'Config/Constants.php'; -require_once BASEPATH . 'Common.php'; +require_once SYSTEMPATH . 'Common.php'; /* * --------------------------------------------------------------- @@ -109,9 +105,9 @@ * that the config files can use the path constants. */ -require_once BASEPATH . 'Autoloader/Autoloader.php'; +require_once SYSTEMPATH . 'Autoloader/Autoloader.php'; require_once APPPATH . 'Config/Autoload.php'; -require_once BASEPATH . 'Config/BaseService.php'; +require_once SYSTEMPATH . 'Config/BaseService.php'; require_once APPPATH . 'Config/Services.php'; // Use Config\Services as CodeIgniter\Services @@ -132,7 +128,7 @@ // Load environment settings from .env files // into $_SERVER and $_ENV -require_once BASEPATH . 'Config/DotEnv.php'; +require_once SYSTEMPATH . 'Config/DotEnv.php'; $env = new \CodeIgniter\Config\DotEnv(ROOTPATH); $env->load(); diff --git a/writable/debugbar/debugbar_1543529344 b/writable/debugbar/debugbar_1543529344 deleted file mode 100644 index 6f5617c..0000000 --- a/writable/debugbar/debugbar_1543529344 +++ /dev/null @@ -1 +0,0 @@ -{"url":"http:\/\/example.com\/","method":"GET","isAJAX":false,"startTime":1543529344.905057,"totalTime":9,"totalMemory":"1.091","segmentDuration":5,"segmentCount":2,"CI_VERSION":"4.0.0-alpha.2","collectors":[{"title":"Timers","titleSafe":"timers","titleDetails":"","display":[],"badgeValue":null,"isEmpty":false,"hasTabContent":false,"hasLabel":false,"icon":"","hasTimelineData":true,"timelineData":[{"name":"Bootstrap","component":"Timer","start":1543529344.90528,"duration":0.005461931228637695},{"name":"Routing","component":"Timer","start":1543529344.910745,"duration":0.00011610984802246094},{"name":"Controller","component":"Timer","start":1543529344.911656,"duration":0.0024101734161376953},{"name":"Controller Constructor","component":"Timer","start":1543529344.911657,"duration":0.0011358261108398438}]},{"title":"Database","titleSafe":"database","titleDetails":"(0 Queries across 0 Connection)","display":{"queries":[]},"badgeValue":0,"isEmpty":true,"hasTabContent":true,"hasLabel":false,"icon":"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADMSURBVEhLY6A3YExLSwsA4nIycQDIDIhRWEBqamo\/UNF\/SjDQjF6ocZgAKPkRiFeEhoYyQ4WIBiA9QAuWAPEHqBAmgLqgHcolGQD1V4DMgHIxwbCxYD+QBqcKINseKo6eWrBioPrtQBq\/BcgY5ht0cUIYbBg2AJKkRxCNWkDQgtFUNJwtABr+F6igE8olGQD114HMgHIxAVDyAhA\/AlpSA8RYUwoeXAPVex5qHCbIyMgwBCkAuQJIY00huDBUz\/mUlBQDqHGjgBjAwAAACexpph6oHSQAAAAASUVORK5CYII=","hasTimelineData":true,"timelineData":[]},{"title":"Logs","titleSafe":"logs","titleDetails":"","display":{"logs":[]},"badgeValue":null,"isEmpty":true,"hasTabContent":true,"hasLabel":false,"icon":"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACYSURBVEhLYxgFJIHU1FSjtLS0i0D8AYj7gEKMEBkqAaAFF4D4ERCvAFrwH4gDoFIMKSkpFkB+OTEYqgUTACXfA\/GqjIwMQyD9H2hRHlQKJFcBEiMGQ7VgAqCBvUgK32dmZspCpagGGNPT0\/1BLqeF4bQHQJePpiIwhmrBBEADR1MRfgB0+WgqAmOoFkwANHA0FY0CUgEDAwCQ0PUpNB3kqwAAAABJRU5ErkJggg==","hasTimelineData":false,"timelineData":[]},{"title":"Views","titleSafe":"views","titleDetails":"","display":[],"badgeValue":1,"isEmpty":false,"hasTabContent":false,"hasLabel":true,"icon":"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADeSURBVEhL7ZSxDcIwEEWNYA0YgGmgyAaJLTcUaaBzQQEVjMEabBQxAdw53zTHiThEovGTfnE\/9rsoRUxhKLOmaa6Uh7X2+UvguLCzVxN1XW9x4EYHzik033Hp3X0LO+DaQG8MDQcuq6qao4qkHuMgQggLvkPLjqh00ZgFDBacMJYFkuwFlH1mshdkZ5JPJERA9JpI6xNCBESvibQ+IURA9JpI6xNCBESvibQ+IURA9DTsuHTOrVFFxixgB\/eUFlU8uKJ0eDBFOu\/9EvoeKnlJS2\/08Tc8NOwQ8sIfMeYFjqKDjdU2sp4AAAAASUVORK5CYII=","hasTimelineData":true,"timelineData":[{"name":"View: welcome_message.php","component":"Views","start":1543529344.913878,"duration":0.00011801719665527344}]},{"title":"Files","titleSafe":"files","titleDetails":"( 83 )","display":{"coreFiles":[{"name":"AutoloadConfig.php","path":"BASEPATH\/Config\/AutoloadConfig.php"},{"name":"Autoloader.php","path":"BASEPATH\/Autoloader\/Autoloader.php"},{"name":"BaseCollector.php","path":"BASEPATH\/Debug\/Toolbar\/Collectors\/BaseCollector.php"},{"name":"BaseConfig.php","path":"BASEPATH\/Config\/BaseConfig.php"},{"name":"BaseService.php","path":"BASEPATH\/Config\/BaseService.php"},{"name":"CacheFactory.php","path":"BASEPATH\/Cache\/CacheFactory.php"},{"name":"CacheInterface.php","path":"BASEPATH\/Cache\/CacheInterface.php"},{"name":"CodeIgniter.php","path":"BASEPATH\/CodeIgniter.php"},{"name":"Common.php","path":"BASEPATH\/Common.php"},{"name":"Config.php","path":"BASEPATH\/Config\/Config.php"},{"name":"Config.php","path":"BASEPATH\/Database\/Config.php"},{"name":"Controller.php","path":"BASEPATH\/Controller.php"},{"name":"Database.php","path":"BASEPATH\/Debug\/Toolbar\/Collectors\/Database.php"},{"name":"DotEnv.php","path":"BASEPATH\/Config\/DotEnv.php"},{"name":"Events.php","path":"BASEPATH\/Debug\/Toolbar\/Collectors\/Events.php"},{"name":"Events.php","path":"BASEPATH\/Events\/Events.php"},{"name":"Exceptions.php","path":"BASEPATH\/Debug\/Exceptions.php"},{"name":"FileHandler.php","path":"BASEPATH\/Cache\/Handlers\/FileHandler.php"},{"name":"FileLocator.php","path":"BASEPATH\/Autoloader\/FileLocator.php"},{"name":"Files.php","path":"BASEPATH\/Debug\/Toolbar\/Collectors\/Files.php"},{"name":"FilterInterface.php","path":"BASEPATH\/Filters\/FilterInterface.php"},{"name":"Filters.php","path":"BASEPATH\/Filters\/Filters.php"},{"name":"Header.php","path":"BASEPATH\/HTTP\/Header.php"},{"name":"IncomingRequest.php","path":"BASEPATH\/HTTP\/IncomingRequest.php"},{"name":"Logger.php","path":"BASEPATH\/Log\/Logger.php"},{"name":"LoggerInterface.php","path":"BASEPATH\/ThirdParty\/PSR\/Log\/LoggerInterface.php"},{"name":"Logs.php","path":"BASEPATH\/Debug\/Toolbar\/Collectors\/Logs.php"},{"name":"Message.php","path":"BASEPATH\/HTTP\/Message.php"},{"name":"RendererInterface.php","path":"BASEPATH\/View\/RendererInterface.php"},{"name":"Request.php","path":"BASEPATH\/HTTP\/Request.php"},{"name":"RequestInterface.php","path":"BASEPATH\/HTTP\/RequestInterface.php"},{"name":"Response.php","path":"BASEPATH\/HTTP\/Response.php"},{"name":"ResponseInterface.php","path":"BASEPATH\/HTTP\/ResponseInterface.php"},{"name":"ResponseTrait.php","path":"BASEPATH\/API\/ResponseTrait.php"},{"name":"RouteCollection.php","path":"BASEPATH\/Router\/RouteCollection.php"},{"name":"RouteCollectionInterface.php","path":"BASEPATH\/Router\/RouteCollectionInterface.php"},{"name":"Router.php","path":"BASEPATH\/Router\/Router.php"},{"name":"RouterInterface.php","path":"BASEPATH\/Router\/RouterInterface.php"},{"name":"Routes.php","path":"BASEPATH\/Config\/Routes.php"},{"name":"Routes.php","path":"BASEPATH\/Debug\/Toolbar\/Collectors\/Routes.php"},{"name":"Services.php","path":"BASEPATH\/Config\/Services.php"},{"name":"Timer.php","path":"BASEPATH\/Debug\/Timer.php"},{"name":"Timers.php","path":"BASEPATH\/Debug\/Toolbar\/Collectors\/Timers.php"},{"name":"Toolbar.php","path":"BASEPATH\/Debug\/Toolbar.php"},{"name":"URI.php","path":"BASEPATH\/HTTP\/URI.php"},{"name":"UserAgent.php","path":"BASEPATH\/HTTP\/UserAgent.php"},{"name":"View.php","path":"BASEPATH\/Config\/View.php"},{"name":"View.php","path":"BASEPATH\/View\/View.php"},{"name":"Views.php","path":"BASEPATH\/Debug\/Toolbar\/Collectors\/Views.php"},{"name":"bootstrap.php","path":"BASEPATH\/bootstrap.php"},{"name":"kint.php","path":"BASEPATH\/ThirdParty\/Kint\/kint.php"},{"name":"rewrite.php","path":"BASEPATH\/Commands\/Server\/rewrite.php"},{"name":"url_helper.php","path":"BASEPATH\/Helpers\/url_helper.php"}],"userFiles":[{"name":"App.php","path":"APPPATH\/Config\/App.php"},{"name":"Autoload.php","path":"APPPATH\/Config\/Autoload.php"},{"name":"Cache.php","path":"APPPATH\/Config\/Cache.php"},{"name":"ClassLoader.php","path":"\/pub7\/htdocs\/CodeIgniter4\/vendor\/composer\/ClassLoader.php"},{"name":"Constants.php","path":"APPPATH\/Config\/Constants.php"},{"name":"Database.php","path":"APPPATH\/Config\/Database.php"},{"name":"DebugToolbar.php","path":"APPPATH\/Filters\/DebugToolbar.php"},{"name":"Events.php","path":"APPPATH\/Config\/Events.php"},{"name":"Exceptions.php","path":"APPPATH\/Config\/Exceptions.php"},{"name":"Filters.php","path":"APPPATH\/Config\/Filters.php"},{"name":"Home.php","path":"APPPATH\/Controllers\/Home.php"},{"name":"Kint.php","path":"\/pub7\/htdocs\/CodeIgniter4\/vendor\/kint-php\/kint\/src\/Kint.php"},{"name":"Logger.php","path":"APPPATH\/Config\/Logger.php"},{"name":"Modules.php","path":"APPPATH\/Config\/Modules.php"},{"name":"Paths.php","path":"APPPATH\/Config\/Paths.php"},{"name":"Routes.php","path":"APPPATH\/Config\/Routes.php"},{"name":"Services.php","path":"APPPATH\/Config\/Services.php"},{"name":"UserAgents.php","path":"APPPATH\/Config\/UserAgents.php"},{"name":"View.php","path":"APPPATH\/Config\/View.php"},{"name":"autoload.php","path":"\/pub7\/htdocs\/CodeIgniter4\/vendor\/autoload.php"},{"name":"autoload_real.php","path":"\/pub7\/htdocs\/CodeIgniter4\/vendor\/composer\/autoload_real.php"},{"name":"autoload_static.php","path":"\/pub7\/htdocs\/CodeIgniter4\/vendor\/composer\/autoload_static.php"},{"name":"deep_copy.php","path":"\/pub7\/htdocs\/CodeIgniter4\/vendor\/myclabs\/deep-copy\/src\/DeepCopy\/deep_copy.php"},{"name":"development.php","path":"APPPATH\/Config\/Boot\/development.php"},{"name":"index.php","path":"FCPATH\/index.php"},{"name":"init.php","path":"\/pub7\/htdocs\/CodeIgniter4\/vendor\/kint-php\/kint\/init.php"},{"name":"init_footer.php","path":"\/pub7\/htdocs\/CodeIgniter4\/vendor\/kint-php\/kint\/init_footer.php"},{"name":"init_header.php","path":"\/pub7\/htdocs\/CodeIgniter4\/vendor\/kint-php\/kint\/init_header.php"},{"name":"init_helpers.php","path":"\/pub7\/htdocs\/CodeIgniter4\/vendor\/kint-php\/kint\/init_helpers.php"},{"name":"welcome_message.php","path":"APPPATH\/Views\/welcome_message.php"}]},"badgeValue":83,"isEmpty":false,"hasTabContent":true,"hasLabel":false,"icon":"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGBSURBVEhL7ZQ9S8NQGIVTBQUncfMfCO4uLgoKbuKQOWg+OkXERRE1IAXrIHbVDrqIDuLiJgj+gro7S3dnpfq88b1FMTE3VZx64HBzzvvZWxKnj15QCcPwCD5HUfSWR+JtzgmtsUcQBEva5IIm9SwSu+95CAWbUuy67qBa32ByZEDpIaZYZSZMjjQuPcQUq8yEyYEb8FSerYeQVGbAFzJkX1PyQWLhgCz0BxTCekC1Wp0hsa6yokzhed4oje6Iz6rlJEkyIKfUEFtITVtQdAibn5rMyaYsMS+a5wTv8qeXMhcU16QZbKgl3hbs+L4\/pnpdc87MElZgq10p5DxGdq8I7xrvUWUKvG3NbSK7ubngYzdJwSsF7TiOh9VOgfcEz1UayNe3JUPM1RWC5GXYgTfc75B4NBmXJnAtTfpABX0iPvEd9ezALwkplCFXcr9styiNOKc1RRZpaPM9tcqBwlWzGY1qPL9wjqRBgF5BH6j8HWh2S7MHlX8PrmbK+k\/8PzjOOzx1D3i1pKTTAAAAAElFTkSuQmCC","hasTimelineData":false,"timelineData":[]},{"title":"Routes","titleSafe":"routes","titleDetails":"","display":{"matchedRoute":[{"directory":"","controller":"\\App\\Controllers\\Home","method":"index","paramCount":0,"truePCount":0,"params":[]}],"routes":[{"from":"\/","to":"\\App\\Controllers\\Home::index"}]},"badgeValue":1,"isEmpty":false,"hasTabContent":true,"hasLabel":false,"icon":"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFDSURBVEhL7ZRNSsNQFIUjVXSiOFEcuQIHDpzpxC0IGYeE\/BEInbWlCHEDLsSiuANdhKDjgm6ggtSJ+l25ldrmmTwIgtgDh\/t37r1J+16cX0dRFMtpmu5pWAkrvYjjOB7AETzStBFW+inxu3KUJMmhludQpoflS1zXban4LYqiO224h6VLTHr8Z+z8EpIHFF9gG78nDVmW7UgTHKjsCyY98QP+pcq+g8Ku2s8G8X3f3\/I8b038WZTp+bO38zxfFd+I6YY6sNUvFlSDk9CRhiAI1jX1I9Cfw7GG1UB8LAuwbU0ZwQnbRDeEN5qqBxZMLtE1ti9LtbREnMIuOXnyIf5rGIb7Wq8HmlZgwYBH7ORTcKH5E4mpjeGt9fBZcHE2GCQ3Vt7oTNPNg+FXLHnSsHkw\/FR+Gg2bB8Ptzrst\/v6C\/wrH+QB+duli6MYJdQAAAABJRU5ErkJggg==","hasTimelineData":false,"timelineData":[]},{"title":"Events","titleSafe":"events","titleDetails":"","display":{"events":{"pre_system":{"event":"pre_system","duration":"0.02","count":1}}},"badgeValue":1,"isEmpty":false,"hasTabContent":true,"hasLabel":false,"icon":"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEASURBVEhL7ZXNDcIwDIVTsRBH1uDQDdquUA6IM1xgCA6MwJUN2hk6AQzAz0vl0ETUxC5VT3zSU5w81\/mRMGZysixbFEVR0jSKNt8geQU9aRpFmp\/keX6AbjZ5oB74vsaN5lSzA4tLSjpBFxsjeSuRy4d2mDdQTWU7YLbXTNN05mKyovj5KL6B7q3hoy3KwdZxBlT+Ipz+jPHrBqOIynZgcZonoukb\/0ckiTHqNvDXtXEAaygRbaB9FvUTjRUHsIYS0QaSp+Dw6wT4hiTmYHOcYZsdLQ2CbXa4ftuuYR4x9vYZgdb4vsFYUdmABMYeukK9\/SUme3KMFQ77+Yfzh8eYF8+orDuDWU5LAAAAAElFTkSuQmCC","hasTimelineData":false,"timelineData":[]}],"vars":{"varData":{"View Data":[]},"headers":{"Host":"localhost:8080","User-Agent":"Mozilla\/5.0 (X11; Linux x86_64; rv:60.0) Gecko\/20100101 Firefox\/60.0","Accept":"text\/html,application\/xhtml+xml,application\/xml;q=0.9,*\/*;q=0.8","Accept-Language":"en-US,en;q=0.5","Accept-Encoding":"gzip, deflate","Connection":"keep-alive","Upgrade-Insecure-Requests":"1"},"request":"HTTP\/1.1","response":{"statusCode":200,"reason":"OK","contentType":"text\/html; charset=UTF-8"}},"config":{"ciVersion":"4.0.0-alpha.2","phpVersion":"7.1.18","phpSAPI":"cli-server","environment":"development","baseURL":"http:\/\/example.com","timezone":"America\/Chicago","locale":"en","cspEnabled":false,"salt":""}} \ No newline at end of file