[
  {
    "path": ".editorconfig",
    "content": "# top-most EditorConfig file\nroot = true\n\n# Unix-style newlines with a newline ending every file\n[*]\nend_of_line = lf\ninsert_final_newline = true\n\n# Matches multiple files with brace expansion notation\n# Set default charset\n[*]\ncharset = utf-8\n\n# Tab indentation (no size specified)\nindent_style = tab\n"
  },
  {
    "path": ".gitattributes",
    "content": "# This file tells which files and directories should be ignored and\n# NOT downloaded when using composer to pull down a project with\n# the --prefer-dist option selected. Used to remove development\n# specific files so user has a clean download.\n\n# git files\n.gitattributes export-ignore\n# .gitignore\n\n# helper config files\n.travis.yml export-ignore\nphpdoc.dist.xml export-ignore\n\n# Misc other files\nreadme.rst\n\n# They don't want all of our tests...\ntests/codeigniter/ export-ignore\ntests/travis/ export-ignore\n\n# User Guide source files and compiled files\nuser_guide_src export-ignore\nuser_guide export-ignore\n"
  },
  {
    "path": ".github/workflows/test-phpunit.yml",
    "content": "name: PHPUnit\n\non: [push, pull_request]\n\npermissions:\n  contents: read\n\njobs:\n  tests:\n    runs-on: ubuntu-22.04\n    if: \"!contains(github.event.head_commit.message, '[ci skip]')\"\n    env:\n      PHP_INI_VALUES: assert.exception=1, zend.assertions=1\n\n    strategy:\n      fail-fast: false\n      matrix:\n        php: [ '8.1', '8.0', '7.4', '7.3', '7.2', '7.1', '7.0', '5.6', '5.5', '5.4']\n        DB: [ 'pdo/mysql', 'pdo/pgsql', 'pdo/sqlite', 'mysqli', 'pgsql', 'sqlite' ]\n        compiler: [ default ]\n        include:\n          - php: '8.1'\n            DB: 'pdo/mysql'\n            compiler: jit\n          - php: '8.1'\n            DB: 'pdo/pgsql'\n            compiler: jit\n          - php: '8.1'\n            DB: 'pdo/sqlite'\n            compiler: jit\n          - php: '8.1'\n            DB: 'mysqli'\n            compiler: jit\n          - php: '8.1'\n            DB: 'pgsql'\n            compiler: jit\n          - php: '8.1'\n            DB: 'sqlite'\n            compiler: jit\n          - php: '8.0'\n            DB: 'pdo/mysql'\n            compiler: jit\n          - php: '8.0'\n            DB: 'pdo/pgsql'\n            compiler: jit\n          - php: '8.0'\n            DB: 'pdo/sqlite'\n            compiler: jit\n          - php: '8.0'\n            DB: 'mysqli'\n            compiler: jit\n          - php: '8.0'\n            DB: 'pgsql'\n            compiler: jit\n          - php: '8.0'\n            DB: 'sqlite'\n            compiler: jit\n          - php: '5.6'\n            DB: 'mysql'\n            compiler: default\n          - php: '5.5'\n            DB: 'mysql'\n            compiler: default\n          - php: '5.4'\n            DB: 'mysql'\n            compiler: default\n\n    services:\n      postgres:\n        image: postgres:12\n        env:\n          POSTGRES_USER: postgres\n          POSTGRES_PASSWORD: postgres\n          POSTGRES_DB: ci_test\n        ports:\n          - 5432:5432\n        options: --health-cmd=pg_isready --health-interval=10s --health-timeout=5s --health-retries=3\n\n      mysql:\n        image: mysql:5.7\n        env:\n          MYSQL_ALLOW_EMPTY_PASSWORD: true\n          MYSQL_USER: travis\n          MYSQL_PASSWORD: travis\n          MYSQL_DATABASE: ci_test\n        ports:\n          - 3306:3306\n        options: --health-cmd=\"mysqladmin ping\" --health-interval=10s --health-timeout=5s --health-retries=3\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v2\n      - name: Override PHP ini values for JIT compiler\n        if: matrix.compiler == 'jit'\n        run: echo \"PHP_INI_VALUES::assert.exception=1, zend.assertions=1, opcache.enable=1, opcache.enable_cli=1, opcache.optimization_level=-1, opcache.jit=1255, opcache.jit_buffer_size=64M\" >> $GITHUB_ENV \n\n      - name: Install PHP${{ matrix.php }} - DB ${{ matrix.DB }}\n        uses: shivammathur/setup-php@v2\n        with:\n          php-version: ${{ matrix.php }}\n          tools: composer, pecl\n          extensions: imagick, sqlite3, pgsql, mysqli, pdo, pdo_mysql, pdo_pgsql, pdo_sqlite, mbstring\n          ini-values: ${{ env.PHP_INI_VALUES }}\n          coverage: xdebug\n\n      - name: Get composer cache directory\n        id: composer-cache\n        run: echo \"::set-output name=dir::$(composer config cache-files-dir)\"\n      - name: Cache composer dependencies\n        uses: actions/cache@v2\n        with:\n          path: ${{ steps.composer-cache.outputs.dir }}\n          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}\n          restore-keys: ${{ runner.os }}-composer-   \n      - name: Install composer dependencies\n        run: composer install --no-progress --prefer-dist --optimize-autoloader\n\n      - name: PHPUnit Test\n        run: |\n          php -d error_reporting=E_ALL -d zend.enable_gc=0 -d date.timezone=UTC -d mbstring.func_overload=7 -d mbstring.internal_encoding=UTF-8 vendor/bin/phpunit --coverage-text --configuration tests/travis/${{ matrix.DB }}.phpunit.xml\n        env:\n          XDEBUG_MODE: coverage\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\n\napplication/cache/*\n!application/cache/index.html\n\napplication/logs/*\n!application/logs/index.html\n\n!application/*/.htaccess\n\ncomposer.lock\ntests/mocks/database/ci_test.sqlite\n\nuser_guide_src/build/*\nuser_guide_src/cilexer/build/*\nuser_guide_src/cilexer/dist/*\nuser_guide_src/cilexer/pycilexer.egg-info/*\n/vendor/\n\n# IDE Files\n#-------------------------\n/nbproject/\n.idea/*\n\n## Sublime Text cache files\n*.tmlanguage.cache\n*.tmPreferences.cache\n*.stTheme.cache\n*.sublime-workspace\n*.sublime-project\n/tests/tests/\n/tests/results/\n"
  },
  {
    "path": "DCO.txt",
    "content": "Developer's Certificate of Origin 1.1\n\nBy making a contribution to this project, I certify that:\n\n(1)\tThe contribution was created in whole or in part by me and I\n\thave the right to submit it under the open source license\n\tindicated in the file; or\n\n(2)\tThe contribution is based upon previous work that, to the best\n\tof my knowledge, is covered under an appropriate open source\n\tlicense and I have the right under that license to submit that\n\twork with modifications, whether created in whole or in part\n\tby me, under the same open source license (unless I am\n\tpermitted to submit under a different license), as indicated\n\tin the file; or\n\n(3)\tThe contribution was provided directly to me by some other\n\tperson who certified (1), (2) or (3) and I have not modified\n\tit.\n\n(4)\tI understand and agree that this project and the contribution\n\tare public and that a record of the contribution (including all\n\tpersonal information I submit with it, including my sign-off) is\n\tmaintained indefinitely and may be redistributed consistent with\n\tthis project or the open source license(s) involved.\n"
  },
  {
    "path": "application/.htaccess",
    "content": "<IfModule authz_core_module>\n    Require all denied\n</IfModule>\n<IfModule !authz_core_module>\n    Deny from all\n</IfModule>"
  },
  {
    "path": "application/cache/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "application/config/autoload.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/*\n| -------------------------------------------------------------------\n| AUTO-LOADER\n| -------------------------------------------------------------------\n| This file specifies which systems should be loaded by default.\n|\n| In order to keep the framework as light-weight as possible only the\n| absolute minimal resources are loaded by default. For example,\n| the database is not connected to automatically since no assumption\n| is made regarding whether you intend to use it.  This file lets\n| you globally define which systems you would like loaded with every\n| request.\n|\n| -------------------------------------------------------------------\n| Instructions\n| -------------------------------------------------------------------\n|\n| These are the things you can load automatically:\n|\n| 1. Packages\n| 2. Libraries\n| 3. Drivers\n| 4. Helper files\n| 5. Custom config files\n| 6. Language files\n| 7. Models\n|\n*/\n\n/*\n| -------------------------------------------------------------------\n|  Auto-load Packages\n| -------------------------------------------------------------------\n| Prototype:\n|\n|  $autoload['packages'] = array(APPPATH.'third_party', '/usr/local/shared');\n|\n*/\n$autoload['packages'] = array();\n\n/*\n| -------------------------------------------------------------------\n|  Auto-load Libraries\n| -------------------------------------------------------------------\n| These are the classes located in system/libraries/ or your\n| application/libraries/ directory, with the addition of the\n| 'database' library, which is somewhat of a special case.\n|\n| Prototype:\n|\n|\t$autoload['libraries'] = array('database', 'email', 'session');\n|\n| You can also supply an alternative library name to be assigned\n| in the controller:\n|\n|\t$autoload['libraries'] = array('user_agent' => 'ua');\n*/\n$autoload['libraries'] = array();\n\n/*\n| -------------------------------------------------------------------\n|  Auto-load Drivers\n| -------------------------------------------------------------------\n| These classes are located in system/libraries/ or in your\n| application/libraries/ directory, but are also placed inside their\n| own subdirectory and they extend the CI_Driver_Library class. They\n| offer multiple interchangeable driver options.\n|\n| Prototype:\n|\n|\t$autoload['drivers'] = array('cache');\n|\n| You can also supply an alternative property name to be assigned in\n| the controller:\n|\n|\t$autoload['drivers'] = array('cache' => 'cch');\n|\n*/\n$autoload['drivers'] = array();\n\n/*\n| -------------------------------------------------------------------\n|  Auto-load Helper Files\n| -------------------------------------------------------------------\n| Prototype:\n|\n|\t$autoload['helper'] = array('url', 'file');\n*/\n$autoload['helper'] = array();\n\n/*\n| -------------------------------------------------------------------\n|  Auto-load Config files\n| -------------------------------------------------------------------\n| Prototype:\n|\n|\t$autoload['config'] = array('config1', 'config2');\n|\n| NOTE: This item is intended for use ONLY if you have created custom\n| config files.  Otherwise, leave it blank.\n|\n*/\n$autoload['config'] = array();\n\n/*\n| -------------------------------------------------------------------\n|  Auto-load Language files\n| -------------------------------------------------------------------\n| Prototype:\n|\n|\t$autoload['language'] = array('lang1', 'lang2');\n|\n| NOTE: Do not include the \"_lang\" part of your file.  For example\n| \"codeigniter_lang.php\" would be referenced as array('codeigniter');\n|\n*/\n$autoload['language'] = array();\n\n/*\n| -------------------------------------------------------------------\n|  Auto-load Models\n| -------------------------------------------------------------------\n| Prototype:\n|\n|\t$autoload['model'] = array('first_model', 'second_model');\n|\n| You can also supply an alternative model name to be assigned\n| in the controller:\n|\n|\t$autoload['model'] = array('first_model' => 'first');\n*/\n$autoload['model'] = array();\n"
  },
  {
    "path": "application/config/config.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/*\n|--------------------------------------------------------------------------\n| Base Site URL\n|--------------------------------------------------------------------------\n|\n| URL to your CodeIgniter root. Typically this will be your base URL,\n| WITH a trailing slash:\n|\n|\thttp://example.com/\n|\n| WARNING: You MUST set this value!\n|\n| If it is not set, then CodeIgniter will try to guess the protocol and\n| path to your installation, but due to security concerns the hostname will\n| be set to $_SERVER['SERVER_ADDR'] if available, or localhost otherwise.\n| The auto-detection mechanism exists only for convenience during\n| development and MUST NOT be used in production!\n|\n| If you need to allow multiple domains, remember that this file is still\n| a PHP script and you can easily do that on your own.\n|\n*/\n$config['base_url'] = '';\n\n/*\n|--------------------------------------------------------------------------\n| Index File\n|--------------------------------------------------------------------------\n|\n| Typically this will be your index.php file, unless you've renamed it to\n| something else. If you are using mod_rewrite to remove the page set this\n| variable so that it is blank.\n|\n*/\n$config['index_page'] = 'index.php';\n\n/*\n|--------------------------------------------------------------------------\n| URI PROTOCOL\n|--------------------------------------------------------------------------\n|\n| This item determines which server global should be used to retrieve the\n| URI string.  The default setting of 'REQUEST_URI' works for most servers.\n| If your links do not seem to work, try one of the other delicious flavors:\n|\n| 'REQUEST_URI'    Uses $_SERVER['REQUEST_URI']\n| 'QUERY_STRING'   Uses $_SERVER['QUERY_STRING']\n| 'PATH_INFO'      Uses $_SERVER['PATH_INFO']\n|\n| WARNING: If you set this to 'PATH_INFO', URIs will always be URL-decoded!\n*/\n$config['uri_protocol']\t= 'REQUEST_URI';\n\n/*\n|--------------------------------------------------------------------------\n| URL suffix\n|--------------------------------------------------------------------------\n|\n| This option allows you to add a suffix to all URLs generated by CodeIgniter.\n| For more information please see the user guide:\n|\n| https://codeigniter.com/userguide3/general/urls.html\n|\n| Note: This option is ignored for CLI requests.\n*/\n$config['url_suffix'] = '';\n\n/*\n|--------------------------------------------------------------------------\n| Default Language\n|--------------------------------------------------------------------------\n|\n| This determines which set of language files should be used. Make sure\n| there is an available translation if you intend to use something other\n| than english.\n|\n*/\n$config['language']\t= 'english';\n\n/*\n|--------------------------------------------------------------------------\n| Default Character Set\n|--------------------------------------------------------------------------\n|\n| This determines which character set is used by default in various methods\n| that require a character set to be provided.\n|\n| See https://secure.php.net/htmlspecialchars for a list of supported charsets.\n|\n*/\n$config['charset'] = 'UTF-8';\n\n/*\n|--------------------------------------------------------------------------\n| Enable/Disable System Hooks\n|--------------------------------------------------------------------------\n|\n| If you would like to use the 'hooks' feature you must enable it by\n| setting this variable to TRUE (boolean).  See the user guide for details.\n|\n*/\n$config['enable_hooks'] = FALSE;\n\n/*\n|--------------------------------------------------------------------------\n| Class Extension Prefix\n|--------------------------------------------------------------------------\n|\n| This item allows you to set the filename/classname prefix when extending\n| native libraries.  For more information please see the user guide:\n|\n| https://codeigniter.com/userguide3/general/core_classes.html\n| https://codeigniter.com/userguide3/general/creating_libraries.html\n|\n*/\n$config['subclass_prefix'] = 'MY_';\n\n/*\n|--------------------------------------------------------------------------\n| Composer auto-loading\n|--------------------------------------------------------------------------\n|\n| Enabling this setting will tell CodeIgniter to look for a Composer\n| package auto-loader script in application/vendor/autoload.php.\n|\n|\t$config['composer_autoload'] = TRUE;\n|\n| Or if you have your vendor/ directory located somewhere else, you\n| can opt to set a specific path as well:\n|\n|\t$config['composer_autoload'] = '/path/to/vendor/autoload.php';\n|\n| For more information about Composer, please visit https://getcomposer.org/\n|\n| Note: This will NOT disable or override the CodeIgniter-specific\n|\tautoloading (application/config/autoload.php)\n*/\n$config['composer_autoload'] = FALSE;\n\n/*\n|--------------------------------------------------------------------------\n| Allowed URL Characters\n|--------------------------------------------------------------------------\n|\n| This lets you specify which characters are permitted within your URLs.\n| When someone tries to submit a URL with disallowed characters they will\n| get a warning message.\n|\n| As a security measure you are STRONGLY encouraged to restrict URLs to\n| as few characters as possible.  By default only these are allowed: a-z 0-9~%.:_-\n|\n| Leave blank to allow all characters -- but only if you are insane.\n|\n| The configured value is actually a regular expression character group\n| and it will be executed as: ! preg_match('/^[<permitted_uri_chars>]+$/i\n|\n| DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!!\n|\n| Note: This option is ignored for CLI requests.\n|\n*/\n$config['permitted_uri_chars'] = 'a-z 0-9~%.:_\\-';\n\n/*\n|--------------------------------------------------------------------------\n| Enable Query Strings\n|--------------------------------------------------------------------------\n|\n| By default CodeIgniter uses search-engine friendly segment based URLs:\n| example.com/who/what/where/\n|\n| You can optionally enable standard query string based URLs:\n| example.com?who=me&what=something&where=here\n|\n| Options are: TRUE or FALSE (boolean)\n|\n| The other items let you set the query string 'words' that will\n| invoke your controllers and its functions:\n| example.com/index.php?c=controller&m=function\n|\n| Please note that some of the helpers won't work as expected when\n| this feature is enabled, since CodeIgniter is designed primarily to\n| use segment based URLs.\n|\n*/\n$config['enable_query_strings'] = FALSE;\n$config['controller_trigger'] = 'c';\n$config['function_trigger'] = 'm';\n$config['directory_trigger'] = 'd';\n\n/*\n|--------------------------------------------------------------------------\n| Error Logging Threshold\n|--------------------------------------------------------------------------\n|\n| You can enable error logging by setting a threshold over zero. The\n| threshold determines what gets logged. Threshold options are:\n|\n|\t0 = Disables logging, Error logging TURNED OFF\n|\t1 = Error Messages (including PHP errors)\n|\t2 = Debug Messages\n|\t3 = Informational Messages\n|\t4 = All Messages\n|\n| You can also pass an array with threshold levels to show individual error types\n|\n| \tarray(2) = Debug Messages, without Error Messages\n|\n| For a live site you'll usually only enable Errors (1) to be logged otherwise\n| your log files will fill up very fast.\n|\n*/\n$config['log_threshold'] = 0;\n\n/*\n|--------------------------------------------------------------------------\n| Error Logging Directory Path\n|--------------------------------------------------------------------------\n|\n| Leave this BLANK unless you would like to set something other than the default\n| application/logs/ directory. Use a full server path.\n|\n*/\n$config['log_path'] = '';\n\n/*\n|--------------------------------------------------------------------------\n| Error Logging FILENAME\n|--------------------------------------------------------------------------\n|\n| Leave this BLANK unless you would like to set something other than the default\n| 'log-'.date('Y-m-d').'.php'. No DIRECTORY_SEPARATOR(s), just the filename.\n|\n*/\n$config['log_filename'] = '';\n\n/*\n|--------------------------------------------------------------------------\n| Log File Permissions\n|--------------------------------------------------------------------------\n|\n| The file system permissions to be applied on newly created log files.\n|\n| IMPORTANT: This MUST be an integer (no quotes) and you MUST use octal\n|            integer notation (i.e. 0700, 0644, etc.)\n*/\n$config['log_file_permissions'] = 0644;\n\n/*\n|--------------------------------------------------------------------------\n| Date Format for Logs\n|--------------------------------------------------------------------------\n|\n| Each item that is logged has an associated date. You can use PHP date\n| codes to set your own date formatting\n|\n*/\n$config['log_date_format'] = 'Y-m-d H:i:s';\n\n/*\n|--------------------------------------------------------------------------\n| Error Views Directory Path\n|--------------------------------------------------------------------------\n|\n| Leave this BLANK unless you would like to set something other than the default\n| application/views/errors/ directory.  Use a full server path.\n|\n*/\n$config['error_views_path'] = '';\n\n/*\n|--------------------------------------------------------------------------\n| Cache Directory Path\n|--------------------------------------------------------------------------\n|\n| Leave this BLANK unless you would like to set something other than the default\n| application/cache/ directory.  Use a full server path.\n|\n*/\n$config['cache_path'] = '';\n\n/*\n|--------------------------------------------------------------------------\n| Cache Include Query String\n|--------------------------------------------------------------------------\n|\n| Whether to take the URL query string into consideration when generating\n| output cache files. Valid options are:\n|\n|\tFALSE      = Disabled\n|\tTRUE       = Enabled, take all query parameters into account.\n|\t             Please be aware that this may result in numerous cache\n|\t             files generated for the same page over and over again.\n|\tarray('q') = Enabled, but only take into account the specified list\n|\t             of query parameters.\n|\n*/\n$config['cache_query_string'] = FALSE;\n\n/*\n|--------------------------------------------------------------------------\n| Encryption Key\n|--------------------------------------------------------------------------\n|\n| If you use the Encryption class, you must set an encryption key.\n| See the user guide for more info.\n|\n| https://codeigniter.com/userguide3/libraries/encryption.html\n|\n*/\n$config['encryption_key'] = '';\n\n/*\n|--------------------------------------------------------------------------\n| Session Variables\n|--------------------------------------------------------------------------\n|\n| 'sess_driver'\n|\n|\tThe storage driver to use: files, database, redis, memcached\n|\n| 'sess_cookie_name'\n|\n|\tThe session cookie name, must contain only [0-9a-z_-] characters\n|\n| 'sess_samesite'\n|\n|\tSession cookie SameSite attribute: Lax (default), Strict or None\n|\n| 'sess_expiration'\n|\n|\tThe number of SECONDS you want the session to last.\n|\tSetting to 0 (zero) means expire when the browser is closed.\n|\n| 'sess_save_path'\n|\n|\tThe location to save sessions to, driver dependent.\n|\n|\tFor the 'files' driver, it's a path to a writable directory.\n|\tWARNING: Only absolute paths are supported!\n|\n|\tFor the 'database' driver, it's a table name.\n|\tPlease read up the manual for the format with other session drivers.\n|\n|\tIMPORTANT: You are REQUIRED to set a valid save path!\n|\n| 'sess_match_ip'\n|\n|\tWhether to match the user's IP address when reading the session data.\n|\n|\tWARNING: If you're using the database driver, don't forget to update\n|\t         your session table's PRIMARY KEY when changing this setting.\n|\n| 'sess_time_to_update'\n|\n|\tHow many seconds between CI regenerating the session ID.\n|\n| 'sess_regenerate_destroy'\n|\n|\tWhether to destroy session data associated with the old session ID\n|\twhen auto-regenerating the session ID. When set to FALSE, the data\n|\twill be later deleted by the garbage collector.\n|\n| Other session cookie settings are shared with the rest of the application,\n| except for 'cookie_prefix' and 'cookie_httponly', which are ignored here.\n|\n*/\n$config['sess_driver'] = 'files';\n$config['sess_cookie_name'] = 'ci_session';\n$config['sess_samesite'] = 'Lax';\n$config['sess_expiration'] = 7200;\n$config['sess_save_path'] = NULL;\n$config['sess_match_ip'] = FALSE;\n$config['sess_time_to_update'] = 300;\n$config['sess_regenerate_destroy'] = FALSE;\n\n/*\n|--------------------------------------------------------------------------\n| Cookie Related Variables\n|--------------------------------------------------------------------------\n|\n| 'cookie_prefix'   = Set a cookie name prefix if you need to avoid collisions\n| 'cookie_domain'   = Set to .your-domain.com for site-wide cookies\n| 'cookie_path'     = Typically will be a forward slash\n| 'cookie_secure'   = Cookie will only be set if a secure HTTPS connection exists.\n| 'cookie_httponly' = Cookie will only be accessible via HTTP(S) (no javascript)\n| 'cookie_samesite' = Cookie's samesite attribute (Lax, Strict or None)\n|\n| Note: These settings (with the exception of 'cookie_prefix' and\n|       'cookie_httponly') will also affect sessions.\n|\n*/\n$config['cookie_prefix']\t= '';\n$config['cookie_domain']\t= '';\n$config['cookie_path']\t\t= '/';\n$config['cookie_secure']\t= FALSE;\n$config['cookie_httponly'] \t= FALSE;\n$config['cookie_samesite'] \t= 'Lax';\n\n/*\n|--------------------------------------------------------------------------\n| Cross Site Request Forgery\n|--------------------------------------------------------------------------\n| Enables a CSRF cookie token to be set. When set to TRUE, token will be\n| checked on a submitted form. If you are accepting user data, it is strongly\n| recommended CSRF protection be enabled.\n|\n| 'csrf_token_name' = The token name\n| 'csrf_cookie_name' = The cookie name\n| 'csrf_expire' = The number in seconds the token should expire.\n| 'csrf_regenerate' = Regenerate token on every submission\n| 'csrf_exclude_uris' = Array of URIs which ignore CSRF checks\n*/\n$config['csrf_protection'] = FALSE;\n$config['csrf_token_name'] = 'csrf_test_name';\n$config['csrf_cookie_name'] = 'csrf_cookie_name';\n$config['csrf_expire'] = 7200;\n$config['csrf_regenerate'] = TRUE;\n$config['csrf_exclude_uris'] = array();\n\n/*\n|--------------------------------------------------------------------------\n| Output Compression\n|--------------------------------------------------------------------------\n|\n| Enables Gzip output compression for faster page loads.  When enabled,\n| the output class will test whether your server supports Gzip.\n| Even if it does, however, not all browsers support compression\n| so enable only if you are reasonably sure your visitors can handle it.\n|\n| Only used if zlib.output_compression is turned off in your php.ini.\n| Please do not use it together with httpd-level output compression.\n|\n| VERY IMPORTANT:  If you are getting a blank page when compression is enabled it\n| means you are prematurely outputting something to your browser. It could\n| even be a line of whitespace at the end of one of your scripts.  For\n| compression to work, nothing can be sent before the output buffer is called\n| by the output class.  Do not 'echo' any values with compression enabled.\n|\n*/\n$config['compress_output'] = FALSE;\n\n/*\n|--------------------------------------------------------------------------\n| Master Time Reference\n|--------------------------------------------------------------------------\n|\n| Options are 'local' or any PHP supported timezone. This preference tells\n| the system whether to use your server's local time as the master 'now'\n| reference, or convert it to the configured one timezone. See the 'date\n| helper' page of the user guide for information regarding date handling.\n|\n*/\n$config['time_reference'] = 'local';\n\n/*\n|--------------------------------------------------------------------------\n| Reverse Proxy IPs\n|--------------------------------------------------------------------------\n|\n| If your server is behind a reverse proxy, you must whitelist the proxy\n| IP addresses from which CodeIgniter should trust headers such as\n| HTTP_X_FORWARDED_FOR and HTTP_CLIENT_IP in order to properly identify\n| the visitor's IP address.\n|\n| You can use both an array or a comma-separated list of proxy addresses,\n| as well as specifying whole subnets. Here are a few examples:\n|\n| Comma-separated:\t'10.0.1.200,192.168.5.0/24'\n| Array:\t\tarray('10.0.1.200', '192.168.5.0/24')\n*/\n$config['proxy_ips'] = '';\n"
  },
  {
    "path": "application/config/constants.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/*\n|--------------------------------------------------------------------------\n| Display Debug backtrace\n|--------------------------------------------------------------------------\n|\n| If set to TRUE, a backtrace will be displayed along with php errors. If\n| error_reporting is disabled, the backtrace will not display, regardless\n| of this setting\n|\n*/\ndefined('SHOW_DEBUG_BACKTRACE') OR define('SHOW_DEBUG_BACKTRACE', TRUE);\n\n/*\n|--------------------------------------------------------------------------\n| File and Directory Modes\n|--------------------------------------------------------------------------\n|\n| These prefs are used when checking and setting modes when working\n| with the file system.  The defaults are fine on servers with proper\n| security, but you may wish (or even need) to change the values in\n| certain environments (Apache running a separate process for each\n| user, PHP under CGI with Apache suEXEC, etc.).  Octal values should\n| always be used to set the mode correctly.\n|\n*/\ndefined('FILE_READ_MODE')  OR define('FILE_READ_MODE', 0644);\ndefined('FILE_WRITE_MODE') OR define('FILE_WRITE_MODE', 0666);\ndefined('DIR_READ_MODE')   OR define('DIR_READ_MODE', 0755);\ndefined('DIR_WRITE_MODE')  OR define('DIR_WRITE_MODE', 0755);\n\n/*\n|--------------------------------------------------------------------------\n| File Stream Modes\n|--------------------------------------------------------------------------\n|\n| These modes are used when working with fopen()/popen()\n|\n*/\ndefined('FOPEN_READ')                           OR define('FOPEN_READ', 'rb');\ndefined('FOPEN_READ_WRITE')                     OR define('FOPEN_READ_WRITE', 'r+b');\ndefined('FOPEN_WRITE_CREATE_DESTRUCTIVE')       OR define('FOPEN_WRITE_CREATE_DESTRUCTIVE', 'wb'); // truncates existing file data, use with care\ndefined('FOPEN_READ_WRITE_CREATE_DESTRUCTIVE')  OR define('FOPEN_READ_WRITE_CREATE_DESTRUCTIVE', 'w+b'); // truncates existing file data, use with care\ndefined('FOPEN_WRITE_CREATE')                   OR define('FOPEN_WRITE_CREATE', 'ab');\ndefined('FOPEN_READ_WRITE_CREATE')              OR define('FOPEN_READ_WRITE_CREATE', 'a+b');\ndefined('FOPEN_WRITE_CREATE_STRICT')            OR define('FOPEN_WRITE_CREATE_STRICT', 'xb');\ndefined('FOPEN_READ_WRITE_CREATE_STRICT')       OR define('FOPEN_READ_WRITE_CREATE_STRICT', 'x+b');\n\n/*\n|--------------------------------------------------------------------------\n| Exit Status Codes\n|--------------------------------------------------------------------------\n|\n| Used to indicate the conditions under which the script is exit()ing.\n| While there is no universal standard for error codes, there are some\n| broad conventions.  Three such conventions are mentioned below, for\n| those who wish to make use of them.  The CodeIgniter defaults were\n| chosen for the least overlap with these conventions, while still\n| leaving room for others to be defined in future versions and user\n| applications.\n|\n| The three main conventions used for determining exit status codes\n| are as follows:\n|\n|    Standard C/C++ Library (stdlibc):\n|       https://www.gnu.org/software/libc/manual/html_node/Exit-Status.html\n|       (This link also contains other GNU-specific conventions)\n|    BSD sysexits.h:\n|       https://www.gsp.com/cgi-bin/man.cgi?section=3&topic=sysexits\n|    Bash scripting:\n|       http://tldp.org/LDP/abs/html/exitcodes.html\n|\n*/\ndefined('EXIT_SUCCESS')        OR define('EXIT_SUCCESS', 0); // no errors\ndefined('EXIT_ERROR')          OR define('EXIT_ERROR', 1); // generic error\ndefined('EXIT_CONFIG')         OR define('EXIT_CONFIG', 3); // configuration error\ndefined('EXIT_UNKNOWN_FILE')   OR define('EXIT_UNKNOWN_FILE', 4); // file not found\ndefined('EXIT_UNKNOWN_CLASS')  OR define('EXIT_UNKNOWN_CLASS', 5); // unknown class\ndefined('EXIT_UNKNOWN_METHOD') OR define('EXIT_UNKNOWN_METHOD', 6); // unknown class member\ndefined('EXIT_USER_INPUT')     OR define('EXIT_USER_INPUT', 7); // invalid user input\ndefined('EXIT_DATABASE')       OR define('EXIT_DATABASE', 8); // database error\ndefined('EXIT__AUTO_MIN')      OR define('EXIT__AUTO_MIN', 9); // lowest automatically-assigned error code\ndefined('EXIT__AUTO_MAX')      OR define('EXIT__AUTO_MAX', 125); // highest automatically-assigned error code\n"
  },
  {
    "path": "application/config/database.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/*\n| -------------------------------------------------------------------\n| DATABASE CONNECTIVITY SETTINGS\n| -------------------------------------------------------------------\n| This file will contain the settings needed to access your database.\n|\n| For complete instructions please consult the 'Database Connection'\n| page of the User Guide.\n|\n| -------------------------------------------------------------------\n| EXPLANATION OF VARIABLES\n| -------------------------------------------------------------------\n|\n|\t['dsn']      The full DSN string describe a connection to the database.\n|\t['hostname'] The hostname of your database server.\n|\t['username'] The username used to connect to the database\n|\t['password'] The password used to connect to the database\n|\t['database'] The name of the database you want to connect to\n|\t['dbdriver'] The database driver. e.g.: mysqli.\n|\t\t\tCurrently supported:\n|\t\t\t\t cubrid, ibase, mssql, mysql, mysqli, oci8,\n|\t\t\t\t odbc, pdo, postgre, sqlite3, sqlsrv\n|\t['dbprefix'] You can add an optional prefix, which will be added\n|\t\t\t\t to the table name when using the  Query Builder class\n|\t['pconnect'] TRUE/FALSE - Whether to use a persistent connection\n|\t['db_debug'] TRUE/FALSE - Whether database errors should be displayed.\n|\t['cache_on'] TRUE/FALSE - Enables/disables query caching\n|\t['cachedir'] The path to the folder where cache files should be stored\n|\t['char_set'] The character set used in communicating with the database\n|\t['dbcollat'] The character collation used in communicating with the database\n|\t\t\t\t NOTE: For MySQL and MySQLi databases, this setting is only used\n| \t\t\t\t as a backup if your server is running PHP < 5.2.3 or MySQL < 5.0.7\n|\t\t\t\t (and in table creation queries made with DB Forge).\n| \t\t\t\t There is an incompatibility in PHP with mysql_real_escape_string() which\n| \t\t\t\t can make your site vulnerable to SQL injection if you are using a\n| \t\t\t\t multi-byte character set and are running versions lower than these.\n| \t\t\t\t Sites using Latin-1 or UTF-8 database character set and collation are unaffected.\n|\t['swap_pre'] A default table prefix that should be swapped with the dbprefix\n|\t['encrypt']  Whether or not to use an encrypted connection.\n|\n|\t\t\t'mysql' (deprecated), 'sqlsrv' and 'pdo/sqlsrv' drivers accept TRUE/FALSE\n|\t\t\t'mysqli' and 'pdo/mysql' drivers accept an array with the following options:\n|\n|\t\t\t\t'ssl_key'    - Path to the private key file\n|\t\t\t\t'ssl_cert'   - Path to the public key certificate file\n|\t\t\t\t'ssl_ca'     - Path to the certificate authority file\n|\t\t\t\t'ssl_capath' - Path to a directory containing trusted CA certificates in PEM format\n|\t\t\t\t'ssl_cipher' - List of *allowed* ciphers to be used for the encryption, separated by colons (':')\n|\t\t\t\t'ssl_verify' - TRUE/FALSE; Whether verify the server certificate or not\n|\n|\t['compress'] Whether or not to use client compression (MySQL only)\n|\t['stricton'] TRUE/FALSE - forces 'Strict Mode' connections\n|\t\t\t\t\t\t\t- good for ensuring strict SQL while developing\n|\t['ssl_options']\tUsed to set various SSL options that can be used when making SSL connections.\n|\t['failover'] array - A array with 0 or more data for connections if the main should fail.\n|\t['save_queries'] TRUE/FALSE - Whether to \"save\" all executed queries.\n| \t\t\t\tNOTE: Disabling this will also effectively disable both\n| \t\t\t\t$this->db->last_query() and profiling of DB queries.\n| \t\t\t\tWhen you run a query, with this setting set to TRUE (default),\n| \t\t\t\tCodeIgniter will store the SQL statement for debugging purposes.\n| \t\t\t\tHowever, this may cause high memory usage, especially if you run\n| \t\t\t\ta lot of SQL queries ... disable this to avoid that problem.\n|\n| The $active_group variable lets you choose which connection group to\n| make active.  By default there is only one group (the 'default' group).\n*/\n$active_group = 'default';\n$db['default'] = array(\n\t'dsn'\t=> '',\n\t'hostname' => 'localhost',\n\t'username' => '',\n\t'password' => '',\n\t'database' => '',\n\t'dbdriver' => 'mysqli',\n\t'dbprefix' => '',\n\t'pconnect' => FALSE,\n\t'db_debug' => (ENVIRONMENT !== 'production'),\n\t'cache_on' => FALSE,\n\t'cachedir' => '',\n\t'char_set' => 'utf8',\n\t'dbcollat' => 'utf8_general_ci',\n\t'swap_pre' => '',\n\t'encrypt' => FALSE,\n\t'compress' => FALSE,\n\t'stricton' => FALSE,\n\t'failover' => array(),\n\t'save_queries' => TRUE\n);\n"
  },
  {
    "path": "application/config/doctypes.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n$_doctypes = array(\n\t'xhtml11' => '<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">',\n\t'xhtml1-strict' => '<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">',\n\t'xhtml1-trans' => '<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">',\n\t'xhtml1-frame' => '<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Frameset//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd\">',\n\t'xhtml-basic11' => '<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML Basic 1.1//EN\" \"http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd\">',\n\t'html5' => '<!DOCTYPE html>',\n\t'html4-strict' => '<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">',\n\t'html4-trans' => '<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">',\n\t'html4-frame' => '<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Frameset//EN\" \"http://www.w3.org/TR/html4/frameset.dtd\">',\n\t'mathml1' => '<!DOCTYPE math SYSTEM \"http://www.w3.org/Math/DTD/mathml1/mathml.dtd\">',\n\t'mathml2' => '<!DOCTYPE math PUBLIC \"-//W3C//DTD MathML 2.0//EN\" \"http://www.w3.org/Math/DTD/mathml2/mathml2.dtd\">',\n\t'svg10' => '<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">',\n\t'svg11' => '<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">',\n\t'svg11-basic' => '<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1 Basic//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-basic.dtd\">',\n\t'svg11-tiny' => '<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1 Tiny//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd\">',\n\t'xhtml-math-svg-xh' => '<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN\" \"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd\">',\n\t'xhtml-math-svg-sh' => '<!DOCTYPE svg:svg PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN\" \"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd\">',\n\t'xhtml-rdfa-1' => '<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML+RDFa 1.0//EN\" \"http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd\">',\n\t'xhtml-rdfa-2' => '<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML+RDFa 1.1//EN\" \"http://www.w3.org/MarkUp/DTD/xhtml-rdfa-2.dtd\">'\n);\n"
  },
  {
    "path": "application/config/foreign_chars.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/*\n| -------------------------------------------------------------------\n| Foreign Characters\n| -------------------------------------------------------------------\n| This file contains an array of foreign characters for transliteration\n| conversion used by the Text helper\n|\n*/\n$foreign_characters = array(\n\t'/ä|æ|ǽ/' => 'ae',\n\t'/ö|œ/' => 'oe',\n\t'/ü/' => 'ue',\n\t'/Ä/' => 'Ae',\n\t'/Ü/' => 'Ue',\n\t'/Ö/' => 'Oe',\n\t'/À|Á|Â|Ã|Ä|Å|Ǻ|Ā|Ă|Ą|Ǎ|Α|Ά|Ả|Ạ|Ầ|Ẫ|Ẩ|Ậ|Ằ|Ắ|Ẵ|Ẳ|Ặ|А/' => 'A',\n\t'/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª|α|ά|ả|ạ|ầ|ấ|ẫ|ẩ|ậ|ằ|ắ|ẵ|ẳ|ặ|а/' => 'a',\n\t'/Б/' => 'B',\n\t'/б/' => 'b',\n\t'/Ç|Ć|Ĉ|Ċ|Č/' => 'C',\n\t'/ç|ć|ĉ|ċ|č/' => 'c',\n\t'/Д|Δ/' => 'D',\n\t'/д|δ/' => 'd',\n\t'/Ð|Ď|Đ/' => 'Dj',\n\t'/ð|ď|đ/' => 'dj',\n\t'/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě|Ε|Έ|Ẽ|Ẻ|Ẹ|Ề|Ế|Ễ|Ể|Ệ|Е|Э/' => 'E',\n\t'/è|é|ê|ë|ē|ĕ|ė|ę|ě|έ|ε|ẽ|ẻ|ẹ|ề|ế|ễ|ể|ệ|е|э/' => 'e',\n\t'/Ф/' => 'F',\n\t'/ф/' => 'f',\n\t'/Ĝ|Ğ|Ġ|Ģ|Γ|Г|Ґ/' => 'G',\n\t'/ĝ|ğ|ġ|ģ|γ|г|ґ/' => 'g',\n\t'/Ĥ|Ħ/' => 'H',\n\t'/ĥ|ħ/' => 'h',\n\t'/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ|Η|Ή|Ί|Ι|Ϊ|Ỉ|Ị|И|Ы/' => 'I',\n\t'/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı|η|ή|ί|ι|ϊ|ỉ|ị|и|ы|ї/' => 'i',\n\t'/Ĵ/' => 'J',\n\t'/ĵ/' => 'j',\n\t'/Θ/' => 'TH',\n\t'/θ/' => 'th',\n\t'/Ķ|Κ|К/' => 'K',\n\t'/ķ|κ|к/' => 'k',\n\t'/Ĺ|Ļ|Ľ|Ŀ|Ł|Λ|Л/' => 'L',\n\t'/ĺ|ļ|ľ|ŀ|ł|λ|л/' => 'l',\n\t'/М/' => 'M',\n\t'/м/' => 'm',\n\t'/Ñ|Ń|Ņ|Ň|Ν|Н/' => 'N',\n\t'/ñ|ń|ņ|ň|ŉ|ν|н/' => 'n',\n\t'/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ|Ο|Ό|Ω|Ώ|Ỏ|Ọ|Ồ|Ố|Ỗ|Ổ|Ộ|Ờ|Ớ|Ỡ|Ở|Ợ|О/' => 'O',\n\t'/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º|ο|ό|ω|ώ|ỏ|ọ|ồ|ố|ỗ|ổ|ộ|ờ|ớ|ỡ|ở|ợ|о/' => 'o',\n\t'/П/' => 'P',\n\t'/п/' => 'p',\n\t'/Ŕ|Ŗ|Ř|Ρ|Р/' => 'R',\n\t'/ŕ|ŗ|ř|ρ|р/' => 'r',\n\t'/Ś|Ŝ|Ş|Ș|Š|Σ|С/' => 'S',\n\t'/ś|ŝ|ş|ș|š|ſ|σ|ς|с/' => 's',\n\t'/Ț|Ţ|Ť|Ŧ|Τ|Т/' => 'T',\n\t'/ț|ţ|ť|ŧ|τ|т/' => 't',\n\t'/Þ|þ/' => 'th',\n\t'/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ|Ũ|Ủ|Ụ|Ừ|Ứ|Ữ|Ử|Ự|У/' => 'U',\n\t'/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ|υ|ύ|ϋ|ủ|ụ|ừ|ứ|ữ|ử|ự|у/' => 'u',\n\t'/Ƴ|Ɏ|Ỵ|Ẏ|Ӳ|Ӯ|Ў|Ý|Ÿ|Ŷ|Υ|Ύ|Ϋ|Ỳ|Ỹ|Ỷ|Ỵ|Й/' => 'Y',\n\t'/ẙ|ʏ|ƴ|ɏ|ỵ|ẏ|ӳ|ӯ|ў|ý|ÿ|ŷ|ỳ|ỹ|ỷ|ỵ|й/' => 'y',\n\t'/В/' => 'V',\n\t'/в/' => 'v',\n\t'/Ŵ/' => 'W',\n\t'/ŵ/' => 'w',\n\t'/Φ/' => 'F',\n\t'/φ/' => 'f',\n\t'/Χ/' => 'CH',\n\t'/χ/' => 'ch',\n\t'/Ź|Ż|Ž|Ζ|З/' => 'Z',\n\t'/ź|ż|ž|ζ|з/' => 'z',\n\t'/Æ|Ǽ/' => 'AE',\n\t'/ß/' => 'ss',\n\t'/Ĳ/' => 'IJ',\n\t'/ĳ/' => 'ij',\n\t'/Œ/' => 'OE',\n\t'/ƒ/' => 'f',\n\t'/Ξ/' => 'KS',\n\t'/ξ/' => 'ks',\n\t'/Π/' => 'P',\n\t'/π/' => 'p',\n\t'/Β/' => 'V',\n\t'/β/' => 'v',\n\t'/Μ/' => 'M',\n\t'/μ/' => 'm',\n\t'/Ψ/' => 'PS',\n\t'/ψ/' => 'ps',\n\t'/Ё/' => 'Yo',\n\t'/ё/' => 'yo',\n\t'/Є/' => 'Ye',\n\t'/є/' => 'ye',\n\t'/Ї/' => 'Yi',\n\t'/Ж/' => 'Zh',\n\t'/ж/' => 'zh',\n\t'/Х/' => 'Kh',\n\t'/х/' => 'kh',\n\t'/Ц/' => 'Ts',\n\t'/ц/' => 'ts',\n\t'/Ч/' => 'Ch',\n\t'/ч/' => 'ch',\n\t'/Ш/' => 'Sh',\n\t'/ш/' => 'sh',\n\t'/Щ/' => 'Shch',\n\t'/щ/' => 'shch',\n\t'/Ъ|ъ|Ь|ь/' => '',\n\t'/Ю/' => 'Yu',\n\t'/ю/' => 'yu',\n\t'/Я/' => 'Ya',\n\t'/я/' => 'ya'\n);\n"
  },
  {
    "path": "application/config/hooks.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/*\n| -------------------------------------------------------------------------\n| Hooks\n| -------------------------------------------------------------------------\n| This file lets you define \"hooks\" to extend CI without hacking the core\n| files.  Please see the user guide for info:\n|\n|\thttps://codeigniter.com/userguide3/general/hooks.html\n|\n*/\n"
  },
  {
    "path": "application/config/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "application/config/memcached.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/*\n| -------------------------------------------------------------------------\n| Memcached settings\n| -------------------------------------------------------------------------\n| Your Memcached servers can be specified below.\n|\n|\tSee: https://codeigniter.com/userguide3/libraries/caching.html#memcached\n|\n*/\n$config = array(\n\t'default' => array(\n\t\t'hostname' => '127.0.0.1',\n\t\t'port'     => '11211',\n\t\t'weight'   => '1',\n\t),\n);\n"
  },
  {
    "path": "application/config/migration.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/*\n|--------------------------------------------------------------------------\n| Enable/Disable Migrations\n|--------------------------------------------------------------------------\n|\n| Migrations are disabled by default for security reasons.\n| You should enable migrations whenever you intend to do a schema migration\n| and disable it back when you're done.\n|\n*/\n$config['migration_enabled'] = FALSE;\n\n/*\n|--------------------------------------------------------------------------\n| Migration Type\n|--------------------------------------------------------------------------\n|\n| Migration file names may be based on a sequential identifier or on\n| a timestamp. Options are:\n|\n|   'sequential' = Sequential migration naming (001_add_blog.php)\n|   'timestamp'  = Timestamp migration naming (20121031104401_add_blog.php)\n|                  Use timestamp format YYYYMMDDHHIISS.\n|\n| Note: If this configuration value is missing the Migration library\n|       defaults to 'sequential' for backward compatibility with CI2.\n|\n*/\n$config['migration_type'] = 'timestamp';\n\n/*\n|--------------------------------------------------------------------------\n| Migrations table\n|--------------------------------------------------------------------------\n|\n| This is the name of the table that will store the current migrations state.\n| When migrations runs it will store in a database table which migration\n| level the system is at. It then compares the migration level in this\n| table to the $config['migration_version'] if they are not the same it\n| will migrate up. This must be set.\n|\n*/\n$config['migration_table'] = 'migrations';\n\n/*\n|--------------------------------------------------------------------------\n| Auto Migrate To Latest\n|--------------------------------------------------------------------------\n|\n| If this is set to TRUE when you load the migrations class and have\n| $config['migration_enabled'] set to TRUE the system will auto migrate\n| to your latest migration (whatever $config['migration_version'] is\n| set to). This way you do not have to call migrations anywhere else\n| in your code to have the latest migration.\n|\n*/\n$config['migration_auto_latest'] = FALSE;\n\n/*\n|--------------------------------------------------------------------------\n| Migrations version\n|--------------------------------------------------------------------------\n|\n| This is used to set migration version that the file system should be on.\n| If you run $this->migration->current() this is the version that schema will\n| be upgraded / downgraded to.\n|\n*/\n$config['migration_version'] = 0;\n\n/*\n|--------------------------------------------------------------------------\n| Migrations Path\n|--------------------------------------------------------------------------\n|\n| Path to your migrations folder.\n| Typically, it will be within your application path.\n| Also, writing permission is required within the migrations path.\n|\n*/\n$config['migration_path'] = APPPATH.'migrations/';\n"
  },
  {
    "path": "application/config/mimes.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/*\n| -------------------------------------------------------------------\n| MIME TYPES\n| -------------------------------------------------------------------\n| This file contains an array of mime types. It is used by the\n| Upload class to help identify allowed file types.\n|\n*/\nreturn array(\n\t'hqx'\t=>\tarray('application/mac-binhex40', 'application/mac-binhex', 'application/x-binhex40', 'application/x-mac-binhex40'),\n\t'cpt'\t=>\t'application/mac-compactpro',\n\t'csv'\t=>\tarray('text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream', 'application/vnd.ms-excel', 'application/x-csv', 'text/x-csv', 'text/csv', 'application/csv', 'application/excel', 'application/vnd.msexcel', 'text/plain'),\n\t'bin'\t=>\tarray('application/macbinary', 'application/mac-binary', 'application/octet-stream', 'application/x-binary', 'application/x-macbinary'),\n\t'dms'\t=>\t'application/octet-stream',\n\t'lha'\t=>\t'application/octet-stream',\n\t'lzh'\t=>\t'application/octet-stream',\n\t'exe'\t=>\tarray('application/octet-stream', 'application/x-msdownload'),\n\t'class'\t=>\t'application/octet-stream',\n\t'psd'\t=>\tarray('application/x-photoshop', 'image/vnd.adobe.photoshop'),\n\t'so'\t=>\t'application/octet-stream',\n\t'sea'\t=>\t'application/octet-stream',\n\t'dll'\t=>\t'application/octet-stream',\n\t'oda'\t=>\t'application/oda',\n\t'pdf'\t=>\tarray('application/pdf', 'application/force-download', 'application/x-download', 'binary/octet-stream'),\n\t'ai'\t=>\tarray('application/pdf', 'application/postscript'),\n\t'eps'\t=>\t'application/postscript',\n\t'ps'\t=>\t'application/postscript',\n\t'smi'\t=>\t'application/smil',\n\t'smil'\t=>\t'application/smil',\n\t'mif'\t=>\t'application/vnd.mif',\n\t'xls'\t=>\tarray('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'),\n\t'ppt'\t=>\tarray('application/powerpoint', 'application/vnd.ms-powerpoint', 'application/vnd.ms-office', 'application/msword'),\n\t'pptx'\t=> \tarray('application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/x-zip', 'application/zip'),\n\t'wbxml'\t=>\t'application/wbxml',\n\t'wmlc'\t=>\t'application/wmlc',\n\t'dcr'\t=>\t'application/x-director',\n\t'dir'\t=>\t'application/x-director',\n\t'dxr'\t=>\t'application/x-director',\n\t'dvi'\t=>\t'application/x-dvi',\n\t'gtar'\t=>\t'application/x-gtar',\n\t'gz'\t=>\t'application/x-gzip',\n\t'gzip'  =>\t'application/x-gzip',\n\t'php'\t=>\tarray('application/x-httpd-php', 'application/php', 'application/x-php', 'text/php', 'text/x-php', 'application/x-httpd-php-source'),\n\t'php4'\t=>\t'application/x-httpd-php',\n\t'php3'\t=>\t'application/x-httpd-php',\n\t'phtml'\t=>\t'application/x-httpd-php',\n\t'phps'\t=>\t'application/x-httpd-php-source',\n\t'js'\t=>\tarray('application/x-javascript', 'text/plain'),\n\t'swf'\t=>\t'application/x-shockwave-flash',\n\t'sit'\t=>\t'application/x-stuffit',\n\t'tar'\t=>\t'application/x-tar',\n\t'tgz'\t=>\tarray('application/x-tar', 'application/x-gzip-compressed'),\n\t'z'\t=>\t'application/x-compress',\n\t'xhtml'\t=>\t'application/xhtml+xml',\n\t'xht'\t=>\t'application/xhtml+xml',\n\t'zip'\t=>\tarray('application/x-zip', 'application/zip', 'application/x-zip-compressed', 'application/s-compressed', 'multipart/x-zip'),\n\t'rar'\t=>\tarray('application/x-rar', 'application/rar', 'application/x-rar-compressed'),\n\t'mid'\t=>\t'audio/midi',\n\t'midi'\t=>\t'audio/midi',\n\t'mpga'\t=>\t'audio/mpeg',\n\t'mp2'\t=>\t'audio/mpeg',\n\t'mp3'\t=>\tarray('audio/mpeg', 'audio/mpg', 'audio/mpeg3', 'audio/mp3'),\n\t'aif'\t=>\tarray('audio/x-aiff', 'audio/aiff'),\n\t'aiff'\t=>\tarray('audio/x-aiff', 'audio/aiff'),\n\t'aifc'\t=>\t'audio/x-aiff',\n\t'ram'\t=>\t'audio/x-pn-realaudio',\n\t'rm'\t=>\t'audio/x-pn-realaudio',\n\t'rpm'\t=>\t'audio/x-pn-realaudio-plugin',\n\t'ra'\t=>\t'audio/x-realaudio',\n\t'rv'\t=>\t'video/vnd.rn-realvideo',\n\t'wav'\t=>\tarray('audio/x-wav', 'audio/wave', 'audio/wav'),\n\t'bmp'\t=>\tarray('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'),\n\t'gif'\t=>\t'image/gif',\n\t'jpeg'\t=>\tarray('image/jpeg', 'image/pjpeg'),\n\t'jpg'\t=>\tarray('image/jpeg', 'image/pjpeg'),\n\t'jpe'\t=>\tarray('image/jpeg', 'image/pjpeg'),\n\t'jp2'\t=>\tarray('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),\n\t'j2k'\t=>\tarray('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),\n\t'jpf'\t=>\tarray('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),\n\t'jpg2'\t=>\tarray('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),\n\t'jpx'\t=>\tarray('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),\n\t'jpm'\t=>\tarray('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),\n\t'mj2'\t=>\tarray('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),\n\t'mjp2'\t=>\tarray('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),\n\t'png'\t=>\tarray('image/png', 'image/x-png'),\n\t'tiff'\t=>\t'image/tiff',\n\t'tif'\t=>\t'image/tiff',\n\t'heic' \t=>\t'image/heic',\n\t'heif' \t=>\t'image/heif',\n\t'css'\t=>\tarray('text/css', 'text/plain'),\n\t'html'\t=>\tarray('text/html', 'text/plain'),\n\t'htm'\t=>\tarray('text/html', 'text/plain'),\n\t'shtml'\t=>\tarray('text/html', 'text/plain'),\n\t'txt'\t=>\t'text/plain',\n\t'text'\t=>\t'text/plain',\n\t'log'\t=>\tarray('text/plain', 'text/x-log'),\n\t'rtx'\t=>\t'text/richtext',\n\t'rtf'\t=>\t'text/rtf',\n\t'xml'\t=>\tarray('application/xml', 'text/xml', 'text/plain'),\n\t'xsl'\t=>\tarray('application/xml', 'text/xsl', 'text/xml'),\n\t'mpeg'\t=>\t'video/mpeg',\n\t'mpg'\t=>\t'video/mpeg',\n\t'mpe'\t=>\t'video/mpeg',\n\t'qt'\t=>\t'video/quicktime',\n\t'mov'\t=>\t'video/quicktime',\n\t'avi'\t=>\tarray('video/x-msvideo', 'video/msvideo', 'video/avi', 'application/x-troff-msvideo'),\n\t'movie'\t=>\t'video/x-sgi-movie',\n\t'doc'\t=>\tarray('application/msword', 'application/vnd.ms-office'),\n\t'docx'\t=>\tarray('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip', 'application/msword', 'application/x-zip'),\n\t'dot'\t=>\tarray('application/msword', 'application/vnd.ms-office'),\n\t'dotx'\t=>\tarray('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip', 'application/msword'),\n\t'xlsx'\t=>\tarray('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/zip', 'application/vnd.ms-excel', 'application/msword', 'application/x-zip'),\n\t'word'\t=>\tarray('application/msword', 'application/octet-stream'),\n\t'xl'\t=>\t'application/excel',\n\t'eml'\t=>\t'message/rfc822',\n\t'json'  =>\tarray('application/json', 'text/json'),\n\t'pem'   =>\tarray('application/x-x509-user-cert', 'application/x-pem-file', 'application/octet-stream'),\n\t'p10'   =>\tarray('application/x-pkcs10', 'application/pkcs10'),\n\t'p12'   =>\t'application/x-pkcs12',\n\t'p7a'   =>\t'application/x-pkcs7-signature',\n\t'p7c'   =>\tarray('application/pkcs7-mime', 'application/x-pkcs7-mime'),\n\t'p7m'   =>\tarray('application/pkcs7-mime', 'application/x-pkcs7-mime'),\n\t'p7r'   =>\t'application/x-pkcs7-certreqresp',\n\t'p7s'   =>\t'application/pkcs7-signature',\n\t'crt'   =>\tarray('application/x-x509-ca-cert', 'application/x-x509-user-cert', 'application/pkix-cert'),\n\t'crl'   =>\tarray('application/pkix-crl', 'application/pkcs-crl'),\n\t'der'   =>\t'application/x-x509-ca-cert',\n\t'kdb'   =>\t'application/octet-stream',\n\t'pgp'   =>\t'application/pgp',\n\t'gpg'   =>\t'application/gpg-keys',\n\t'sst'   =>\t'application/octet-stream',\n\t'csr'   =>\t'application/octet-stream',\n\t'rsa'   =>\t'application/x-pkcs7',\n\t'cer'   =>\tarray('application/pkix-cert', 'application/x-x509-ca-cert'),\n\t'3g2'   =>\t'video/3gpp2',\n\t'3gp'   =>\tarray('video/3gp', 'video/3gpp'),\n\t'mp4'   =>\t'video/mp4',\n\t'm4a'   =>\t'audio/x-m4a',\n\t'f4v'   =>\tarray('video/mp4', 'video/x-f4v'),\n\t'flv'\t=>\t'video/x-flv',\n\t'webm'\t=>\t'video/webm',\n\t'aac'   =>\tarray('audio/x-aac', 'audio/aac'),\n\t'm4u'   =>\t'application/vnd.mpegurl',\n\t'm3u'   =>\t'text/plain',\n\t'xspf'  =>\t'application/xspf+xml',\n\t'vlc'   =>\t'application/videolan',\n\t'wmv'   =>\tarray('video/x-ms-wmv', 'video/x-ms-asf'),\n\t'au'    =>\t'audio/x-au',\n\t'ac3'   =>\t'audio/ac3',\n\t'flac'  =>\t'audio/x-flac',\n\t'ogg'   =>\tarray('audio/ogg', 'video/ogg', 'application/ogg'),\n\t'kmz'\t=>\tarray('application/vnd.google-earth.kmz', 'application/zip', 'application/x-zip'),\n\t'kml'\t=>\tarray('application/vnd.google-earth.kml+xml', 'application/xml', 'text/xml'),\n\t'ics'\t=>\t'text/calendar',\n\t'ical'\t=>\t'text/calendar',\n\t'zsh'\t=>\t'text/x-scriptzsh',\n\t'7z'\t=>\tarray('application/x-7z-compressed', 'application/x-compressed', 'application/x-zip-compressed', 'application/zip', 'multipart/x-zip'),\n\t'7zip'\t=>\tarray('application/x-7z-compressed', 'application/x-compressed', 'application/x-zip-compressed', 'application/zip', 'multipart/x-zip'),\n\t'cdr'\t=>\tarray('application/cdr', 'application/coreldraw', 'application/x-cdr', 'application/x-coreldraw', 'image/cdr', 'image/x-cdr', 'zz-application/zz-winassoc-cdr'),\n\t'wma'\t=>\tarray('audio/x-ms-wma', 'video/x-ms-asf'),\n\t'jar'\t=>\tarray('application/java-archive', 'application/x-java-application', 'application/x-jar', 'application/x-compressed'),\n\t'svg'\t=>\tarray('image/svg+xml', 'image/svg', 'application/xml', 'text/xml'),\n\t'vcf'\t=>\t'text/x-vcard',\n\t'srt'\t=>\tarray('text/srt', 'text/plain'),\n\t'vtt'\t=>\tarray('text/vtt', 'text/plain'),\n\t'ico'\t=>\tarray('image/x-icon', 'image/x-ico', 'image/vnd.microsoft.icon'),\n\t'odc'\t=>\t'application/vnd.oasis.opendocument.chart',\n\t'otc'\t=>\t'application/vnd.oasis.opendocument.chart-template',\n\t'odf'\t=>\t'application/vnd.oasis.opendocument.formula',\n\t'otf'\t=>\t'application/vnd.oasis.opendocument.formula-template',\n\t'odg'\t=>\t'application/vnd.oasis.opendocument.graphics',\n\t'otg'\t=>\t'application/vnd.oasis.opendocument.graphics-template',\n\t'odi'\t=>\t'application/vnd.oasis.opendocument.image',\n\t'oti'\t=>\t'application/vnd.oasis.opendocument.image-template',\n\t'odp'\t=>\t'application/vnd.oasis.opendocument.presentation',\n\t'otp'\t=>\t'application/vnd.oasis.opendocument.presentation-template',\n\t'ods'\t=>\t'application/vnd.oasis.opendocument.spreadsheet',\n\t'ots'\t=>\t'application/vnd.oasis.opendocument.spreadsheet-template',\n\t'odt'\t=>\t'application/vnd.oasis.opendocument.text',\n\t'odm'\t=>\t'application/vnd.oasis.opendocument.text-master',\n\t'ott'\t=>\t'application/vnd.oasis.opendocument.text-template',\n\t'oth'\t=>\t'application/vnd.oasis.opendocument.text-web'\n);\n"
  },
  {
    "path": "application/config/profiler.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/*\n| -------------------------------------------------------------------------\n| Profiler Sections\n| -------------------------------------------------------------------------\n| This file lets you determine whether or not various sections of Profiler\n| data are displayed when the Profiler is enabled.\n| Please see the user guide for info:\n|\n|\thttps://codeigniter.com/userguide3/general/profiling.html\n|\n*/\n"
  },
  {
    "path": "application/config/routes.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/*\n| -------------------------------------------------------------------------\n| URI ROUTING\n| -------------------------------------------------------------------------\n| This file lets you re-map URI requests to specific controller functions.\n|\n| Typically there is a one-to-one relationship between a URL string\n| and its corresponding controller class/method. The segments in a\n| URL normally follow this pattern:\n|\n|\texample.com/class/method/id/\n|\n| In some instances, however, you may want to remap this relationship\n| so that a different class/function is called than the one\n| corresponding to the URL.\n|\n| Please see the user guide for complete details:\n|\n|\thttps://codeigniter.com/userguide3/general/routing.html\n|\n| -------------------------------------------------------------------------\n| RESERVED ROUTES\n| -------------------------------------------------------------------------\n|\n| There are three reserved routes:\n|\n|\t$route['default_controller'] = 'welcome';\n|\n| This route indicates which controller class should be loaded if the\n| URI contains no data. In the above example, the \"welcome\" class\n| would be loaded.\n|\n|\t$route['404_override'] = 'errors/page_missing';\n|\n| This route will tell the Router which controller/method to use if those\n| provided in the URL cannot be matched to a valid route.\n|\n|\t$route['translate_uri_dashes'] = FALSE;\n|\n| This is not exactly a route, but allows you to automatically route\n| controller and method names that contain dashes. '-' isn't a valid\n| class or method name character, so it requires translation.\n| When you set this option to TRUE, it will replace ALL dashes with\n| underscores in the controller and method URI segments.\n|\n| Examples:\tmy-controller/index\t-> my_controller/index\n|\t\tmy-controller/my-method\t-> my_controller/my_method\n*/\n$route['default_controller'] = 'welcome';\n$route['404_override'] = '';\n$route['translate_uri_dashes'] = FALSE;\n"
  },
  {
    "path": "application/config/user_agents.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/*\n| -------------------------------------------------------------------\n| USER AGENT TYPES\n| -------------------------------------------------------------------\n| This file contains four arrays of user agent data. It is used by the\n| User Agent Class to help identify browser, platform, robot, and\n| mobile device data. The array keys are used to identify the device\n| and the array values are used to set the actual name of the item.\n*/\n$platforms = array(\n\t'windows nt 10.0'\t=> 'Windows 10',\n\t'windows nt 6.3'\t=> 'Windows 8.1',\n\t'windows nt 6.2'\t=> 'Windows 8',\n\t'windows nt 6.1'\t=> 'Windows 7',\n\t'windows nt 6.0'\t=> 'Windows Vista',\n\t'windows nt 5.2'\t=> 'Windows 2003',\n\t'windows nt 5.1'\t=> 'Windows XP',\n\t'windows nt 5.0'\t=> 'Windows 2000',\n\t'windows nt 4.0'\t=> 'Windows NT 4.0',\n\t'winnt4.0'\t\t\t=> 'Windows NT 4.0',\n\t'winnt 4.0'\t\t\t=> 'Windows NT',\n\t'winnt'\t\t\t\t=> 'Windows NT',\n\t'windows 98'\t\t=> 'Windows 98',\n\t'win98'\t\t\t\t=> 'Windows 98',\n\t'windows 95'\t\t=> 'Windows 95',\n\t'win95'\t\t\t\t=> 'Windows 95',\n\t'windows phone'\t\t\t=> 'Windows Phone',\n\t'windows'\t\t\t=> 'Unknown Windows OS',\n\t'android'\t\t\t=> 'Android',\n\t'blackberry'\t\t=> 'BlackBerry',\n\t'iphone'\t\t\t=> 'iOS',\n\t'ipad'\t\t\t\t=> 'iOS',\n\t'ipod'\t\t\t\t=> 'iOS',\n\t'os x'\t\t\t\t=> 'Mac OS X',\n\t'ppc mac'\t\t\t=> 'Power PC Mac',\n\t'freebsd'\t\t\t=> 'FreeBSD',\n\t'ppc'\t\t\t\t=> 'Macintosh',\n\t'linux'\t\t\t\t=> 'Linux',\n\t'debian'\t\t\t=> 'Debian',\n\t'sunos'\t\t\t\t=> 'Sun Solaris',\n\t'beos'\t\t\t\t=> 'BeOS',\n\t'apachebench'\t\t=> 'ApacheBench',\n\t'aix'\t\t\t\t=> 'AIX',\n\t'irix'\t\t\t\t=> 'Irix',\n\t'osf'\t\t\t\t=> 'DEC OSF',\n\t'hp-ux'\t\t\t\t=> 'HP-UX',\n\t'netbsd'\t\t\t=> 'NetBSD',\n\t'bsdi'\t\t\t\t=> 'BSDi',\n\t'openbsd'\t\t\t=> 'OpenBSD',\n\t'gnu'\t\t\t\t=> 'GNU/Linux',\n\t'unix'\t\t\t\t=> 'Unknown Unix OS',\n\t'symbian' \t\t\t=> 'Symbian OS'\n);\n\n\n// The order of this array should NOT be changed. Many browsers return\n// multiple browser types so we want to identify the sub-type first.\n$browsers = array(\n\t'OPR'\t\t\t=> 'Opera',\n\t'Flock'\t\t\t=> 'Flock',\n\t'Edge'\t\t\t=> 'Edge',\n\t'Chrome'\t\t=> 'Chrome',\n\t// Opera 10+ always reports Opera/9.80 and appends Version/<real version> to the user agent string\n\t'Opera.*?Version'\t=> 'Opera',\n\t'Opera'\t\t\t=> 'Opera',\n\t'MSIE'\t\t\t=> 'Internet Explorer',\n\t'Internet Explorer'\t=> 'Internet Explorer',\n\t'Trident.* rv'\t=> 'Internet Explorer',\n\t'Shiira'\t\t=> 'Shiira',\n\t'Firefox'\t\t=> 'Firefox',\n\t'Chimera'\t\t=> 'Chimera',\n\t'Phoenix'\t\t=> 'Phoenix',\n\t'Firebird'\t\t=> 'Firebird',\n\t'Camino'\t\t=> 'Camino',\n\t'Netscape'\t\t=> 'Netscape',\n\t'OmniWeb'\t\t=> 'OmniWeb',\n\t'Safari'\t\t=> 'Safari',\n\t'Mozilla'\t\t=> 'Mozilla',\n\t'Konqueror'\t\t=> 'Konqueror',\n\t'icab'\t\t\t=> 'iCab',\n\t'Lynx'\t\t\t=> 'Lynx',\n\t'Links'\t\t\t=> 'Links',\n\t'hotjava'\t\t=> 'HotJava',\n\t'amaya'\t\t\t=> 'Amaya',\n\t'IBrowse'\t\t=> 'IBrowse',\n\t'Maxthon'\t\t=> 'Maxthon',\n\t'Ubuntu'\t\t=> 'Ubuntu Web Browser',\n\t'Vivaldi'\t\t=> 'Vivaldi'\n);\n\n$mobiles = array(\n\t// legacy array, old values commented out\n\t'mobileexplorer'\t=> 'Mobile Explorer',\n//  'openwave'\t\t\t=> 'Open Wave',\n//\t'opera mini'\t\t=> 'Opera Mini',\n//\t'operamini'\t\t\t=> 'Opera Mini',\n//\t'elaine'\t\t\t=> 'Palm',\n\t'palmsource'\t\t=> 'Palm',\n//\t'digital paths'\t\t=> 'Palm',\n//\t'avantgo'\t\t\t=> 'Avantgo',\n//\t'xiino'\t\t\t\t=> 'Xiino',\n\t'palmscape'\t\t\t=> 'Palmscape',\n//\t'nokia'\t\t\t\t=> 'Nokia',\n//\t'ericsson'\t\t\t=> 'Ericsson',\n//\t'blackberry'\t\t=> 'BlackBerry',\n//\t'motorola'\t\t\t=> 'Motorola'\n\n\t// Phones and Manufacturers\n\t'motorola'\t\t=> 'Motorola',\n\t'nokia'\t\t\t=> 'Nokia',\n\t'nexus'\t\t\t=> 'Nexus',\n\t'palm'\t\t\t=> 'Palm',\n\t'iphone'\t\t=> 'Apple iPhone',\n\t'ipad'\t\t\t=> 'iPad',\n\t'ipod'\t\t\t=> 'Apple iPod Touch',\n\t'sony'\t\t\t=> 'Sony Ericsson',\n\t'ericsson'\t\t=> 'Sony Ericsson',\n\t'blackberry'\t=> 'BlackBerry',\n\t'cocoon'\t\t=> 'O2 Cocoon',\n\t'blazer'\t\t=> 'Treo',\n\t'lg'\t\t\t=> 'LG',\n\t'amoi'\t\t\t=> 'Amoi',\n\t'xda'\t\t\t=> 'XDA',\n\t'mda'\t\t\t=> 'MDA',\n\t'vario'\t\t\t=> 'Vario',\n\t'htc'\t\t\t=> 'HTC',\n\t'samsung'\t\t=> 'Samsung',\n\t'sharp'\t\t\t=> 'Sharp',\n\t'sie-'\t\t\t=> 'Siemens',\n\t'alcatel'\t\t=> 'Alcatel',\n\t'benq'\t\t\t=> 'BenQ',\n\t'ipaq'\t\t\t=> 'HP iPaq',\n\t'mot-'\t\t\t=> 'Motorola',\n\t'playstation portable'\t=> 'PlayStation Portable',\n\t'playstation 3'\t\t=> 'PlayStation 3',\n\t'playstation vita'  \t=> 'PlayStation Vita',\n\t'hiptop'\t\t=> 'Danger Hiptop',\n\t'nec-'\t\t\t=> 'NEC',\n\t'panasonic'\t\t=> 'Panasonic',\n\t'philips'\t\t=> 'Philips',\n\t'sagem'\t\t\t=> 'Sagem',\n\t'sanyo'\t\t\t=> 'Sanyo',\n\t'spv'\t\t\t=> 'SPV',\n\t'zte'\t\t\t=> 'ZTE',\n\t'sendo'\t\t\t=> 'Sendo',\n\t'nintendo dsi'\t=> 'Nintendo DSi',\n\t'nintendo ds'\t=> 'Nintendo DS',\n\t'nintendo 3ds'\t=> 'Nintendo 3DS',\n\t'wii'\t\t\t=> 'Nintendo Wii',\n\t'open web'\t\t=> 'Open Web',\n\t'openweb'\t\t=> 'OpenWeb',\n\t'meizu'                 => 'Meizu',\n\t'huawei'                => 'Huawei',\n\t'xiaomi'                => 'Xiaomi',\n\t'oppo'                  => 'Oppo',\n\t'vivo'                  => 'Vivo',\n\t'infinix'               => 'Infinix',\n\n\t// Operating Systems\n\t'android'\t\t=> 'Android',\n\t'symbian'\t\t=> 'Symbian',\n\t'SymbianOS'\t\t=> 'SymbianOS',\n\t'elaine'\t\t=> 'Palm',\n\t'series60'\t\t=> 'Symbian S60',\n\t'windows ce'\t=> 'Windows CE',\n\n\t// Browsers\n\t'obigo'\t\t\t=> 'Obigo',\n\t'netfront'\t\t=> 'Netfront Browser',\n\t'openwave'\t\t=> 'Openwave Browser',\n\t'mobilexplorer'\t=> 'Mobile Explorer',\n\t'operamini'\t\t=> 'Opera Mini',\n\t'opera mini'\t=> 'Opera Mini',\n\t'opera mobi'\t=> 'Opera Mobile',\n\t'fennec'\t\t=> 'Firefox Mobile',\n\n\t// Other\n\t'digital paths'\t=> 'Digital Paths',\n\t'avantgo'\t\t=> 'AvantGo',\n\t'xiino'\t\t\t=> 'Xiino',\n\t'novarra'\t\t=> 'Novarra Transcoder',\n\t'vodafone'\t\t=> 'Vodafone',\n\t'docomo'\t\t=> 'NTT DoCoMo',\n\t'o2'\t\t\t=> 'O2',\n\n\t// Fallback\n\t'mobile'\t\t=> 'Generic Mobile',\n\t'wireless'\t\t=> 'Generic Mobile',\n\t'j2me'\t\t\t=> 'Generic Mobile',\n\t'midp'\t\t\t=> 'Generic Mobile',\n\t'cldc'\t\t\t=> 'Generic Mobile',\n\t'up.link'\t\t=> 'Generic Mobile',\n\t'up.browser'\t=> 'Generic Mobile',\n\t'smartphone'\t=> 'Generic Mobile',\n\t'cellphone'\t\t=> 'Generic Mobile'\n);\n\n// There are hundreds of bots but these are the most common.\n$robots = array(\n\t'googlebot'\t\t=> 'Googlebot',\n\t'msnbot'\t\t=> 'MSNBot',\n\t'baiduspider'\t\t=> 'Baiduspider',\n\t'bingbot'\t\t=> 'Bing',\n\t'slurp'\t\t\t=> 'Inktomi Slurp',\n\t'yahoo'\t\t\t=> 'Yahoo',\n\t'ask jeeves'\t\t=> 'Ask Jeeves',\n\t'fastcrawler'\t\t=> 'FastCrawler',\n\t'infoseek'\t\t=> 'InfoSeek Robot 1.0',\n\t'lycos'\t\t\t=> 'Lycos',\n\t'yandex'\t\t=> 'YandexBot',\n\t'mediapartners-google'\t=> 'MediaPartners Google',\n\t'CRAZYWEBCRAWLER'\t=> 'Crazy Webcrawler',\n\t'adsbot-google'\t\t=> 'AdsBot Google',\n\t'feedfetcher-google'\t=> 'Feedfetcher Google',\n\t'curious george'\t=> 'Curious George',\n\t'ia_archiver'\t\t=> 'Alexa Crawler',\n\t'MJ12bot'\t\t=> 'Majestic-12',\n\t'Uptimebot'\t\t=> 'Uptimebot',\n\t'UptimeRobot'\t\t=> 'UptimeRobot'\n);\n"
  },
  {
    "path": "application/controllers/Welcome.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\nclass Welcome extends CI_Controller {\n\n\t/**\n\t * Index Page for this controller.\n\t *\n\t * Maps to the following URL\n\t * \t\thttp://example.com/index.php/welcome\n\t *\t- or -\n\t * \t\thttp://example.com/index.php/welcome/index\n\t *\t- or -\n\t * Since this controller is set as the default controller in\n\t * config/routes.php, it's displayed at http://example.com/\n\t *\n\t * So any other public methods not prefixed with an underscore will\n\t * map to /index.php/welcome/<method_name>\n\t * @see https://codeigniter.com/userguide3/general/urls.html\n\t */\n\tpublic function index()\n\t{\n\t\t$this->load->view('welcome_message');\n\t}\n}\n"
  },
  {
    "path": "application/controllers/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "application/core/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "application/helpers/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "application/hooks/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "application/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "application/language/english/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "application/language/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "application/libraries/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "application/logs/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "application/models/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "application/third_party/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "application/views/errors/cli/error_404.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\necho \"\\nERROR: \",\n\t$heading,\n\t\"\\n\\n\",\n\t$message,\n\t\"\\n\\n\";"
  },
  {
    "path": "application/views/errors/cli/error_db.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\necho \"\\nDatabase error: \",\n\t$heading,\n\t\"\\n\\n\",\n\t$message,\n\t\"\\n\\n\";"
  },
  {
    "path": "application/views/errors/cli/error_exception.php",
    "content": "<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?>\n\nAn uncaught Exception was encountered\n\nType:        <?php echo get_class($exception), \"\\n\"; ?>\nMessage:     <?php echo $message, \"\\n\"; ?>\nFilename:    <?php echo $exception->getFile(), \"\\n\"; ?>\nLine Number: <?php echo $exception->getLine(); ?>\n\n<?php if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE === TRUE): ?>\n\nBacktrace:\n<?php\tforeach ($exception->getTrace() as $error): ?>\n<?php\t\tif (isset($error['file']) && strpos($error['file'], realpath(BASEPATH)) !== 0): ?>\n\tFile: <?php echo $error['file'], \"\\n\"; ?>\n\tLine: <?php echo $error['line'], \"\\n\"; ?>\n\tFunction: <?php echo $error['function'], \"\\n\\n\"; ?>\n<?php\t\tendif ?>\n<?php\tendforeach ?>\n\n<?php endif ?>\n"
  },
  {
    "path": "application/views/errors/cli/error_general.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\necho \"\\nERROR: \",\n\t$heading,\n\t\"\\n\\n\",\n\t$message,\n\t\"\\n\\n\";"
  },
  {
    "path": "application/views/errors/cli/error_php.php",
    "content": "<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?>\n\nA PHP Error was encountered\n\nSeverity:    <?php echo $severity, \"\\n\"; ?>\nMessage:     <?php echo $message, \"\\n\"; ?>\nFilename:    <?php echo $filepath, \"\\n\"; ?>\nLine Number: <?php echo $line; ?>\n\n<?php if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE === TRUE): ?>\n\nBacktrace:\n<?php\tforeach (debug_backtrace() as $error): ?>\n<?php\t\tif (isset($error['file']) && strpos($error['file'], realpath(BASEPATH)) !== 0): ?>\n\tFile: <?php echo $error['file'], \"\\n\"; ?>\n\tLine: <?php echo $error['line'], \"\\n\"; ?>\n\tFunction: <?php echo $error['function'], \"\\n\\n\"; ?>\n<?php\t\tendif ?>\n<?php\tendforeach ?>\n\n<?php endif ?>\n"
  },
  {
    "path": "application/views/errors/cli/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "application/views/errors/html/error_404.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n?><!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<meta charset=\"utf-8\">\n\t<title>404 Page Not Found</title>\n\t<style type=\"text/css\">\n\n\t::selection { background-color: #f07746; color: #fff; }\n\t::-moz-selection { background-color: #f07746; color: #fff; }\n\n\tbody {\n\t\tbackground-color: #fff;\n\t\tmargin: 40px auto;\n\t\tmax-width: 1024px;\n\t\tfont: 16px/24px normal \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n\t\tcolor: #808080;\n\t}\n\n\ta {\n\t\tcolor: #dd4814;\n\t\tbackground-color: transparent;\n\t\tfont-weight: normal;\n\t\ttext-decoration: none;\n\t}\n\n\ta:hover {\n\t\tcolor: #97310e;\n\t}\n\n\th1 {\n\t\tcolor: #fff;\n\t\tbackground-color: #dd4814;\n\t\tborder-bottom: 1px solid #d0d0d0;\n\t\tfont-size: 22px;\n\t\tfont-weight: bold;\n\t\tmargin: 0 0 14px 0;\n\t\tpadding: 5px 15px;\n\t\tline-height: 40px;\n\t}\n\n\th2 {\n\t\tcolor:#404040;\n\t\tmargin:0;\n\t\tpadding:0 0 10px 0;\n\t}\n\n\tcode {\n\t\tfont-family: Consolas, Monaco, Courier New, Courier, monospace;\n\t\tfont-size: 13px;\n\t\tbackground-color: #f5f5f5;\n\t\tborder: 1px solid #e3e3e3;\n\t\tborder-radius: 4px;\n\t\tcolor: #002166;\n\t\tdisplay: block;\n\t\tmargin: 14px 0 14px 0;\n\t\tpadding: 12px 10px 12px 10px;\n\t}\n\n\t#container {\n\t\tmargin: 10px;\n\t\tborder: 1px solid #d0d0d0;\n\t\tbox-shadow: 0 0 8px #d0d0d0;\n\t\tborder-radius: 4px;\n\t}\n\n\tp {\n\t\tmargin: 0 0 10px;\n\t\tpadding:0;\n\t}\n\n\t#body {\n\t\tmargin: 0 15px 0 15px;\n\t\tmin-height: 96px;\n\t}\n\t</style>\n</head>\n<body>\n\t<div id=\"container\">\n\t\t<h1><?php echo $heading; ?></h1>\n\t\t<div id=\"body\">\n\t\t\t<?php echo $message; ?>\n\t\t</div>\n\t</div>\n</body>\n</html>\n"
  },
  {
    "path": "application/views/errors/html/error_db.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n?><!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<meta charset=\"utf-8\">\n\t<title>Database Error</title>\n\t<style type=\"text/css\">\n\n\t::selection { background-color: #f07746; color: #fff; }\n\t::-moz-selection { background-color: #f07746; color: #fff; }\n\n\tbody {\n\t\tbackground-color: #fff;\n\t\tmargin: 40px auto;\n\t\tmax-width: 1024px;\n\t\tfont: 16px/24px normal \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n\t\tcolor: #808080;\n\t}\n\n\ta {\n\t\tcolor: #dd4814;\n\t\tbackground-color: transparent;\n\t\tfont-weight: normal;\n\t\ttext-decoration: none;\n\t}\n\n\ta:hover {\n\t\tcolor: #97310e;\n\t}\n\n\th1 {\n\t\tcolor: #fff;\n\t\tbackground-color: #dd4814;\n\t\tborder-bottom: 1px solid #d0d0d0;\n\t\tfont-size: 22px;\n\t\tfont-weight: bold;\n\t\tmargin: 0 0 14px 0;\n\t\tpadding: 5px 15px;\n\t\tline-height: 40px;\n\t}\n\n\th2 {\n\t\tcolor:#404040;\n\t\tmargin:0;\n\t\tpadding:0 0 10px 0;\n\t}\n\n\tcode {\n\t\tfont-family: Consolas, Monaco, Courier New, Courier, monospace;\n\t\tfont-size: 13px;\n\t\tbackground-color: #f5f5f5;\n\t\tborder: 1px solid #e3e3e3;\n\t\tborder-radius: 4px;\n\t\tcolor: #002166;\n\t\tdisplay: block;\n\t\tmargin: 14px 0 14px 0;\n\t\tpadding: 12px 10px 12px 10px;\n\t}\n\n\t#container {\n\t\tmargin: 10px;\n\t\tborder: 1px solid #d0d0d0;\n\t\tbox-shadow: 0 0 8px #d0d0d0;\n\t\tborder-radius: 4px;\n\t}\n\n\tp {\n\t\tmargin: 0 0 10px;\n\t\tpadding:0;\n\t}\n\n\t#body {\n\t\tmargin: 0 15px 0 15px;\n\t\tmin-height: 96px;\n\t}\n\t</style>\n</head>\n<body>\n\t<div id=\"container\">\n\t\t<h1><?php echo $heading; ?></h1>\n\t\t<div id=\"body\">\n\t\t\t<?php echo $message; ?>\n\t\t</div>\n\t</div>\n</body>\n</html>\n"
  },
  {
    "path": "application/views/errors/html/error_exception.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n?>\n\n<div style=\"border:1px solid #dd4814;padding-left:20px;margin:10px 0;\">\n\n\t<h4>An uncaught Exception was encountered</h4>\n\n\t<p>Type: <?php echo get_class($exception); ?></p>\n\t<p>Message: <?php echo $message; ?></p>\n\t<p>Filename: <?php echo $exception->getFile(); ?></p>\n\t<p>Line Number: <?php echo $exception->getLine(); ?></p>\n\n\t<?php if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE === TRUE): ?>\n\n\t\t<p>Backtrace:</p>\n\t\t<?php foreach ($exception->getTrace() as $error): ?>\n\n\t\t\t<?php if (isset($error['file']) && strpos($error['file'], realpath(BASEPATH)) !== 0): ?>\n\n\t\t\t\t<p style=\"margin-left:10px\">\n\t\t\t\tFile: <?php echo $error['file']; ?><br />\n\t\t\t\tLine: <?php echo $error['line']; ?><br />\n\t\t\t\tFunction: <?php echo $error['function']; ?>\n\t\t\t\t</p>\n\t\t\t<?php endif ?>\n\n\t\t<?php endforeach ?>\n\n\t<?php endif ?>\n\n</div>\n"
  },
  {
    "path": "application/views/errors/html/error_general.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n?><!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<meta charset=\"utf-8\">\n\t<title>Error</title>\n\t<style type=\"text/css\">\n\n\t::selection { background-color: #f07746; color: #fff; }\n\t::-moz-selection { background-color: #f07746; color: #fff; }\n\n\tbody {\n\t\tbackground-color: #fff;\n\t\tmargin: 40px auto;\n\t\tmax-width: 1024px;\n\t\tfont: 16px/24px normal \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n\t\tcolor: #808080;\n\t}\n\n\ta {\n\t\tcolor: #dd4814;\n\t\tbackground-color: transparent;\n\t\tfont-weight: normal;\n\t\ttext-decoration: none;\n\t}\n\n\ta:hover {\n\t\tcolor: #97310e;\n\t}\n\n\th1 {\n\t\tcolor: #fff;\n\t\tbackground-color: #dd4814;\n\t\tborder-bottom: 1px solid #d0d0d0;\n\t\tfont-size: 22px;\n\t\tfont-weight: bold;\n\t\tmargin: 0 0 14px 0;\n\t\tpadding: 5px 15px;\n\t\tline-height: 40px;\n\t}\n\n\th2 {\n\t\tcolor:#404040;\n\t\tmargin:0;\n\t\tpadding:0 0 10px 0;\n\t}\n\n\tcode {\n\t\tfont-family: Consolas, Monaco, Courier New, Courier, monospace;\n\t\tfont-size: 13px;\n\t\tbackground-color: #f5f5f5;\n\t\tborder: 1px solid #e3e3e3;\n\t\tborder-radius: 4px;\n\t\tcolor: #002166;\n\t\tdisplay: block;\n\t\tmargin: 14px 0 14px 0;\n\t\tpadding: 12px 10px 12px 10px;\n\t}\n\n\t#container {\n\t\tmargin: 10px;\n\t\tborder: 1px solid #d0d0d0;\n\t\tbox-shadow: 0 0 8px #d0d0d0;\n\t\tborder-radius: 4px;\n\t}\n\n\tp {\n\t\tmargin: 0 0 10px;\n\t\tpadding:0;\n\t}\n\n\t#body {\n\t\tmargin: 0 15px 0 15px;\n\t\tmin-height: 96px;\n\t}\n\t</style>\n</head>\n<body>\n\t<div id=\"container\">\n\t\t<h1><?php echo $heading; ?></h1>\n\t\t<div id=\"body\">\n\t\t\t<?php echo $message; ?>\n\t\t</div>\n\t</div>\n</body>\n</html>\n"
  },
  {
    "path": "application/views/errors/html/error_php.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n?>\n\n<div style=\"border:1px solid #dd4814;padding-left:20px;margin:10px 0;\">\n\n\t<h4>A PHP Error was encountered</h4>\n\n\t<p>Severity: <?php echo $severity; ?></p>\n\t<p>Message:  <?php echo $message; ?></p>\n\t<p>Filename: <?php echo $filepath; ?></p>\n\t<p>Line Number: <?php echo $line; ?></p>\n\n\t<?php if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE === TRUE): ?>\n\n\t\t<p>Backtrace:</p>\n\t\t<?php foreach (debug_backtrace() as $error): ?>\n\n\t\t\t<?php if (isset($error['file']) && strpos($error['file'], realpath(BASEPATH)) !== 0): ?>\n\n\t\t\t\t<p style=\"margin-left:10px\">\n\t\t\t\tFile: <?php echo $error['file'] ?><br />\n\t\t\t\tLine: <?php echo $error['line'] ?><br />\n\t\t\t\tFunction: <?php echo $error['function'] ?>\n\t\t\t\t</p>\n\n\t\t\t<?php endif ?>\n\n\t\t<?php endforeach ?>\n\n\t<?php endif ?>\n\n</div>\n"
  },
  {
    "path": "application/views/errors/html/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "application/views/errors/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "application/views/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "application/views/welcome_message.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n?><!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<meta charset=\"utf-8\">\n\t<title>Welcome to CodeIgniter</title>\n\n\t<style type=\"text/css\">\n\n\t::selection { background-color: #f07746; color: #fff; }\n\t::-moz-selection { background-color: #f07746; color: #fff; }\n\n\tbody {\n\t\tbackground-color: #fff;\n\t\tmargin: 40px auto;\n\t\tmax-width: 1024px;\n\t\tfont: 16px/24px normal \"Helvetica Neue\",Helvetica,Arial,sans-serif;\n\t\tcolor: #808080;\n\t}\n\n\ta {\n\t\tcolor: #dd4814;\n\t\tbackground-color: transparent;\n\t\tfont-weight: normal;\n\t\ttext-decoration: none;\n\t}\n\n\ta:hover {\n\t\tcolor: #97310e;\n\t}\n\n\th1 {\n\t\tcolor: #fff;\n\t\tbackground-color: #dd4814;\n\t\tborder-bottom: 1px solid #d0d0d0;\n\t\tfont-size: 22px;\n\t\tfont-weight: bold;\n\t\tmargin: 0 0 14px 0;\n\t\tpadding: 5px 10px;\n\t\tline-height: 40px;\n\t}\n\n\th1 img {\n\t\tdisplay: block;\n\t}\n\n\th2 {\n\t\tcolor:#404040;\n\t\tmargin:0;\n\t\tpadding:0 0 10px 0;\n\t}\n\n\tcode {\n\t\tfont-family: Consolas, Monaco, Courier New, Courier, monospace;\n\t\tfont-size: 13px;\n\t\tbackground-color: #f5f5f5;\n\t\tborder: 1px solid #e3e3e3;\n\t\tborder-radius: 4px;\n\t\tcolor: #002166;\n\t\tdisplay: block;\n\t\tmargin: 14px 0 14px 0;\n\t\tpadding: 12px 10px 12px 10px;\n\t}\n\n\t#body {\n\t\tmargin: 0 15px 0 15px;\n\t\tmin-height: 96px;\n\t}\n\n\tp {\n\t\tmargin: 0 0 10px;\n\t\tpadding:0;\n\t}\n\n\tp.footer {\n\t\ttext-align: right;\n\t\tfont-size: 12px;\n\t\tborder-top: 1px solid #d0d0d0;\n\t\tline-height: 32px;\n\t\tpadding: 0 10px 0 10px;\n\t\tmargin: 20px 0 0 0;\n\t\tbackground:#8ba8af;\n\t\tcolor:#fff;\n\t}\n\n\t#container {\n\t\tmargin: 10px;\n\t\tborder: 1px solid #d0d0d0;\n\t\tbox-shadow: 0 0 8px #d0d0d0;\n\t\tborder-radius: 4px;\n\t}\n\t</style>\n</head>\n<body>\n\t<div id=\"container\">\n\t\t<h1>\n\t\t\t<img alt=\"CodeIgniter\" src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAAAoCAYAAABXadAKAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAGYktHRAD/AP8A/6C9p5MAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQffCAUNIB84RgWtAAAMfklEQVR42u1ce3BVxRnfvc+E8KwEggoFAV/xVURri61Yxc7ojLa1Wm1t1To4tRanD5W2tnqtnTJ1Rmm11lG0D3yH0qpBq9KOqVjFQhWQCJqACcEQYgIJyT2v3f19/aPnwmZz7r0nN0ETPd8MM+Hs6zu//e23337fnstYJJFE0l+Q7ebo6T4Bjp2M0Ijkw5LYkHVUMY54InEiY+zkCNZIRjyhGWOMJVNJnkw9QErOjqCNZMQTmoAUi8WqGY/9hpRMRfBGMmKFpOAk5Sr6vygS3pURKpGMXEIr8XkiZCknStXBtsZGyEQy8sgsxHgCVlMfgQOr96QInUhGlMC1OSm5lAIErvvVCKFIRhahpXcb5RMpvxEhFMnIILJtJclz78hLZgLBts6MkIpk+PvMjj2WhFhOhQSqGVbvtAitSIb5AdCbTFLWUhGBECvgOOURYpEMXzLb1lRSso5CCDznhgixSD5oSYS3zGIG4+whFovPK16ZMQZ6O4I3kg9aQqW+SYpKFovdx+KJeaF65XkOkj3dh5OdHfNRA1EptUjfnfbt2/eJj2wwAHh9/y4M/HvEEZqUSrNY7G4Wiy0YSMc8mZjf72FZ+fUsmX4OnvvpoX4Rz/PmAFgKYCOAPQAEgN0AnpVSnn0wQeScz9yPF9HesWPH7hnE4vhpIVeuqakp/SFzZpb2d6Oh+yUAMgAySqlFw9NvVupWAoo4zFrK+0DUbjdJcSE5dpI8l8N1ziCo7X79LnjuNUOhX09PzyEAVhZSTyn19YNstZ7WrNa6QfZ1b36Y0fFhcmHPnj1jAbyc+6eUusrQfZ2m64vDkMzyPAIcKsyW1STFnXlmYB8p+XdSahUBHf3KXWcJhFvyjT/btqcCaDIm3fWt9AYAXQB629vbKw4yoeu18R8fZF9LfMJ0a312+c8eGubuSKem8/3DjcwTifBmkWCGhGPPI889hYB9VIoodUsp+jU1NaWBA/oBkABu6ezs3O+j19XVxT3PO/5g4pTJZDgAS9Pjl0NEjne0Ph8d7r51V1fXOGNXvPGDGDe0sSLP/UlxMqIZtlXOGGMEdRMRqRIobZNSl5bga95sAPitsG2FEKcC+COA7QAcAN0A1iilrsxkMjzfAlJK3Qhgs9+mE8BKKeV8Q49+12allGcDWAFgp7+D7AKwzLKsw4LGamhoSAEQWp83B9Xr7OwcA+A2AA1+v+8DeEoIcbpu4YUQn9cWyksHbibIL/lYPOLr5PiY/La1tbW8kH8PAC0tLWV+nzsLe6T402AwMXQ+V0p5JoB/AbAAPFbcOljZKpJyU3FCy3pYvftDf+Q61xLw3oApDdSTEFVhCdna2loOoEsDbFWYdnV1dXEAdxUBf0VNTU3MtD4AXs1TX+j/18lTX1+fBLC8wFittm1PDTjgHmsskovNOpZlHQZgaxh4Lcs6VCPHe9r4bwLBByQAdwUQ60GtvCW3qIpPL34xGEwMnZ8BoDRsri9OaM+9nIgQgondsHo/aVj2k0mKmgFz2vOuG4B1vqTvHSi5IOQ2fo8BXheANwD0GgT6ntHuL0a7Xt9P7y1Cnj8Y7Zp0N8l/9nCA9fqKXsfzvBMC3JyXjX72+To5xvOsYQhglANAvX4O8J/vDsCvTiuvY4wx13Vn+/69eZZ5JXd4lFJeUComQTr7dd4C8IoQ4rTCk25n4xDe/aGZKL1f9XNX7Gw5uc6lBNUQ3peW/4RjzwxJzPu0F7PWr19fNEEkpTzDAOSe+vr6JGOM9fb2TgLwrlZWX6Ddqpzf1t7eXgHgWV2XnMsipfyC6VPmypRSP9Ta9JhujrG1q9zWrpVfZOj054aGhpT2Lpu1sk2a5a822q12XfeooIULAAG4t2jlDxpld+oLPs8cDBiTAJ07BhSGhW19lpTcOAB3YS9JeW6gH+7YU0iKZUTkheinmzzvByEJ/V/tBV8L2eYZrc3bdXV1caM8ExTvBfCIbu2y2exko93jWvlm7fkq7fl6w6U4UR+rq6trnNHncq3ttoB3+YdWvqutrW2UUf4frfyvGqHOz+ce+e2W5iNlS0tLmW4plVI3FQhdbsgzBwPGxNRZSvnFASVWeDz+GcZjMwaQVRjPYrHfkZTH9SsqK9/FE8mFzPMWMaK9RfoZSzF+dMhR9YNDa8iT8Dla8uPh+fPnqz6Lj6jPFjtx4sTc52M6gC9WVFSYW/FsM9HgH6jO0RIvJ+uTkkwmN2jjqsbGxqzR5zHa31tNYjHGztAePVVVVWWFSX7oCSD/cNxotJuu/d2sF0yaNGkG51zfScyFNjNfwmUwmJg6W5a1bmCZwlhiFuN8dGFzp9Yxwg6NjDMYYz8mIQJv1/F0+j4mxUJG1JU/rEKMcx42ZawDi2KVJ0yY8CnOeUID7LWAbN8krVw0Nzd32bY9lXN+iFYtCMzZ5iRXVlaexDkP+4M7LXPnzpXGs6PyEbqqquo4413WmokmzvkErbwxT0bTuv3223eFJWU8Hp9pWNttuk/PGDuiEKFLxcTQubtYFjYRQJVyRsQZz3MhQ3oPk5R38HTZE30XQmwOA8YxxuxABqbSK0nJSYzHf1+ApyJs+JExliNg0TvXnPNKYzJ2B1Q7Rft7U3V1tRBCTDaseB8CZLPZyZzzMVr5Nn+8w4x2mxhjPXnUe8OMXhh9bjXepaBOZWVls43yxjyE3Z7JZCgsoU1Ladv2fkIvXrz4cM55mTZmQ8AclIrJzAK7QnFCE9RunkjKQLITbWTEr2NEghG1Mc6O1MqaGGNWQWLFE/eSlAtYPP7lANZJUmpHSEKvZYzlXJw5rusemU6n3ymUIe+79mJTGGMbNRIdyhg7WwP7b7k/jX76fMWeSqVm5SFP1lhAP0skErVhXiyVSh1ttN1aYHdinPPxxrvNLuBW5CWsZVlTOOej8iyEgpYymUzONHDYHvBqpWLSZxEWq9zfh1ZYzwhtAWQWjLCYp1J7Y6NG9zIpf86UfIlBNTIlX2REGZ5I7Ct+OlP3MsZUwPNtTMkXQobtlmtAx5LJ5ErP8441t17P805kjDHHcTYSEWmTvigXFchms5PLysoe45ync5Nl2/Yyf5K3G5N6aUdHx+icLxuPx79mnOK3+QecjQbJFvf09EzUn3V0dIzORVmMMfoQ2nXdrQYRTJ0uyx1wm5qa0pzzBRqx3Nra2p25GLzhI/exdgGkbCxArG2FdkDdfcvF9EvBpJjOIaMc2QmQot9lH0jxPFynT/iIXGscHPsYsq3RoVPqwqskper7f+Hi/RpWNvT9bABPBAXlAawDsAOA0u9VAKgx6rb5MWjLCCVdZoyzxmi3x2/XYyZY9PAhgCeNcsu/X/I6gGYASgjxOW2Rft+P2+4w2r0M4DkjBt1o1GkGsEa/S+E/35Jr5zjOdOM9v2MYiSv0csdxZpg7hdZvjZn1M7EA8DaAdinl+aViEqDz1aXdI3Cd7/b/SYKh+QKFXHsMCa/WYGI7HOfUgfTT0dExWo8B58k6rc7V7+3trQSwpUBdWyn17YA0+WnIc0FLzxIC6GPRzHhwnluA12iTvaKAbm8ZBDpfz5YVaFertTmrUDIKwG36BS89rFlTUxMD4GrlS8woEoC24A//D4wzUEwCdD6rVELPIKK3+mby3G8OHaHFc0NxQSmTyXCl1EUAVvkWVwDIAtgC4AEhxOnm3Qel1M0ANvnWIetnye50HGd6gbsf8wC8BMD2s4SvK6WuV0rdoE3y80GhKqXUjwC86mclpX8HZI1S6gY9/gzglQLEfCEgSXGevxs5vk5rlVILAezV2i3VLPDVRSzwo3qc3jgATjNItzAgXX88gCcBtPvv2QFgjeu6R5aKSTGdB0Y8Ka/Q099wnWuHhtDuNNIyTgT1NCkZ/WTYEIhlWVOGZIv+KAoJESelDtxzFl4NOfagv5bwF0oO8VWk5KQI7aER82sX8w5IRGqlUiTlgR+TkeKswfUnZxDRe0QE8ry7yXPHRCgPXKSUFwBYopS6Skp5oVLqcgAPAJCa2/BGhFQ+InrOVQS1k4B3SaljS+rDsY8hKdYSsI1c5+II1dJFvxORx+d2i95C+9iT2rGmkxTLSKkadO2Zqxq3xENtg1s2jCao+SS8+0mIW+Ha4yM0Sxf/PnFvATJvNg/DHycJHfflZaOaGGMLSYq5LBabwzhrYowV/WiTiKYzomlMqQwvK2+NKDk4qa6uFtlsdlY6nT6Fc34EY2wMY6ybMdaulNqSSqU2fZzxSZTQ5l1GVJnvtzf6M5oJRrSdMXo/ouPQSEVFRRtjrDZCor/8DypCy4UfldJKAAAAAElFTkSuQmCC\"/>\n\t\t</h1>\n\n\t\t<div id=\"body\">\n\t\t\t<p>The page you are looking at is being generated dynamically by CodeIgniter.</p>\n\n\t\t\t<p>If you would like to edit this page you'll find it located at:</p>\n\t\t\t<code>application/views/welcome_message.php</code>\n\n\t\t\t<p>The corresponding controller for this page is found at:</p>\n\t\t\t<code>application/controllers/Welcome.php</code>\n\n\t\t\t<p>If you are exploring CodeIgniter for the very first time, you should start by reading the <a href=\"user_guide/\">User Guide</a>.</p>\n\t\t</div>\n\n\t\t<p class=\"footer\">Page rendered in <strong>{elapsed_time}</strong> seconds. <?php echo  (ENVIRONMENT === 'development') ?  'CodeIgniter Version <strong>' . CI_VERSION . '</strong>' : '' ?></p>\n\t</div>\n</body>\n</html>\n"
  },
  {
    "path": "build-release.sh",
    "content": "#!/usr/bin/env bash\n\ncd $(dirname $BASH_SOURCE)\n\nif [ $# -eq 0 ]; then\n\techo 'Usage: '$BASH_SOURCE' <version_number>'\n\texit 1\nfi\n\nversion_number=$1\n\nif [ ${#version_number} -lt 5 ]\nthen\n\techo \"Provided version number is too short\"\n\texit 1\nelif [ ${version_number: -4} == \"-dev\" ]\nthen\n\techo \"'-dev' releases are not allowed\"\n\texit 1\nfi\n\nversion_id=${version_number:0:5}\nversion_id=${version_id//./}\nupgrade_rst='user_guide_src/source/installation/upgrade_'$version_id'.rst'\n\nif [ ${#version_id} -ne 3 ]\nthen\n\techo \"Invalid version number format\"\n\texit 1\nelif [ `grep -c -F --regexp=\"'$version_number'\" system/core/CodeIgniter.php` -ne 1 ]\nthen\n\techo \"Provided version number doesn't match in system/core/CodeIgniter.php\"\n\texit 1\nelif [ `grep -c -F --regexp=\"'$version_number'\" user_guide_src/source/conf.py` -ne 2 ]\nthen\n\techo \"Provided version number doesn't match in user_guide_src/source/conf.py\"\n\texit 1\nelif [ `grep -c -F --regexp=\"$version_number (Current version) <https://codeload.github.com/bcit-ci/CodeIgniter/zip/$version_number>\" user_guide_src/source/installation/downloads.rst` -ne 1 ]\nthen\n\techo \"user_guide_src/source/installation/downloads.rst doesn't appear to contain a link for this version\"\n\texit 1\nelif [ ! -f \"$upgrade_rst\" ]\nthen\n\techo \"${upgrade_rst} doesn't exist\"\n\texit 1\nfi\n\necho \"Running tests ...\"\n\nphp -d zend.enable_gc=0 -d date.timezone=UTC -d mbstring.func_overload=7 -d mbstring.internal_encoding=UTF-8 vendor/bin/phpunit --coverage-text --configuration tests/travis/sqlite.phpunit.xml\n\nif [ $? -ne 0 ]\nthen\n\techo \"Build FAILED!\"\n\texit 1\nfi\n\ncd user_guide_src/\n\necho \"\"\necho \"Building HTML docs; please check output for warnings ...\"\necho \"\"\n\nmake html\n\necho \"\"\n\nif [ $? -ne 0 ]\nthen\n\techo \"Build FAILED!\"\n\texit 1\nfi\n\ncd ..\n\nif [ -d user_guide/ ]\nthen\n\trm -r user_guide/\nfi\n\ncp -r user_guide_src/build/html/ user_guide/\ngit add user_guide/\n\necho \"Build complete.\"\n"
  },
  {
    "path": "composer.json",
    "content": "{\n\t\"description\": \"The CodeIgniter framework\",\n\t\"name\": \"codeigniter/framework\",\n\t\"type\": \"project\",\n\t\"homepage\": \"https://codeigniter.com\",\n\t\"license\": \"MIT\",\n\t\"support\": {\n\t\t\"forum\": \"https://forum.codeigniter.com/\",\n\t\t\"wiki\": \"https://github.com/bcit-ci/CodeIgniter/wiki\",\n\t\t\"slack\": \"https://codeigniterchat.slack.com\",\n\t\t\"source\": \"https://github.com/bcit-ci/CodeIgniter\"\n\t},\n\t\"require\": {\n\t\t\"php\": \">=5.4.8\"\n\t},\n\t\"suggest\": {\n\t\t\"paragonie/random_compat\": \"Provides better randomness in PHP 5.x\"\n\t},\n\t\"scripts\": {\n\t\t\"test:coverage\": [\n\t\t\t\"@putenv XDEBUG_MODE=coverage\",\n\t\t\t\"phpunit --color=always --coverage-text --configuration tests/travis/sqlite.phpunit.xml\"\n\t\t],\n\t\t\"post-install-cmd\": [\n\t\t\t\"sed -i s/name{0}/name[0]/ vendor/mikey179/vfsstream/src/main/php/org/bovigo/vfs/vfsStream.php\"\n\t\t],\n\t\t\"post-update-cmd\": [\n\t\t\t\"sed -i s/name{0}/name[0]/ vendor/mikey179/vfsstream/src/main/php/org/bovigo/vfs/vfsStream.php\"\n\t\t]\n\t},\n\t\"require-dev\": {\n\t\t\"mikey179/vfsstream\": \"1.6.*\",\n\t\t\"phpunit/phpunit\": \"4.* || 5.* || 9.*\"\n\t}\n}\n"
  },
  {
    "path": "contributing.md",
    "content": "# Contributing to CodeIgniter\n\nCodeIgniter is a community driven project and accepts contributions of code and documentation from the community. These contributions are made in the form of Issues or [Pull Requests](http://help.github.com/send-pull-requests/) on the [CodeIgniter repository](https://github.com/bcit-ci/CodeIgniter) on GitHub.\n\nIssues are a quick way to point out a bug. If you find a bug or documentation error in CodeIgniter then please check a few things first:\n\n1. There is not already an open Issue\n2. The issue has already been fixed (check the develop branch, or look for closed Issues)\n3. Is it something really obvious that you can fix yourself?\n\nReporting issues is helpful but an even better approach is to send a Pull Request, which is done by \"Forking\" the main repository and committing to your own copy. This will require you to use the version control system called Git.\n\n## Guidelines\n\nBefore we look into how, here are the guidelines. If your Pull Requests fail\nto pass these guidelines it will be declined and you will need to re-submit\nwhen you’ve made the changes. This might sound a bit tough, but it is required\nfor us to maintain quality of the code-base.\n\n### PHP Style\n\nAll code must meet the [Style Guide](https://codeigniter.com/userguide3/general/styleguide.html), which is\nessentially the [Allman indent style](https://en.wikipedia.org/wiki/Indent_style#Allman_style), underscores and readable operators. This makes certain that all code is the same format as the existing code and means it will be as readable as possible.\n\n### Documentation\n\nIf you change anything that requires a change to documentation then you will need to add it. New classes, methods, parameters, changing default values, etc are all things that will require a change to documentation. The change-log must also be updated for every change. Also PHPDoc blocks must be maintained.\n\n### Compatibility\n\nCodeIgniter recommends PHP 5.5 or newer to be used, but it should be\ncompatible with PHP 5.2.4 so all code supplied must stick to this\nrequirement. If PHP 5.3 (and above) functions or features are used then\nthere must be a fallback for PHP 5.2.4.\n\n### Branching\n\nCodeIgniter uses the [Git-Flow](https://nvie.com/posts/a-successful-git-branching-model/) branching model which requires all pull requests to be sent to the \"develop\" branch. This is\nwhere the next planned version will be developed. The \"master\" branch will always contain the latest stable version and is kept clean so a \"hotfix\" (e.g: an emergency security patch) can be applied to master to create a new version, without worrying about other features holding it up. For this reason all commits need to be made to \"develop\" and any sent to \"master\" will be closed automatically. If you have multiple changes to submit, please place all changes into their own branch on your fork.\n\nOne thing at a time: A pull request should only contain one change. That does not mean only one commit, but one change - however many commits it took. The reason for this is that if you change X and Y but send a pull request for both at the same time, we might really want X but disagree with Y, meaning we cannot merge the request. Using the Git-Flow branching model you can create new branches for both of these features and send two requests.\n\n### Signing\n\nYou must sign your work, certifying that you either wrote the work or otherwise have the right to pass it on to an open source project. git makes this trivial as you merely have to use `--signoff` on your commits to your CodeIgniter fork.\n\n`git commit --signoff`\n\nor simply\n\n`git commit -s`\n\nThis will sign your commits with the information setup in your git config, e.g.\n\n`Signed-off-by: John Q Public <john.public@example.com>`\n\nIf you are using [Tower](https://www.git-tower.com/) there is a \"Sign-Off\" checkbox in the commit window. You could even alias git commit to use the `-s` flag so you don’t have to think about it.\n\nBy signing your work in this manner, you certify to a \"Developer's Certificate of Origin\". The current version of this certificate is in the `DCO.txt` file in the root of this repository.\n\n## How-to Guide\n\nThere are two ways to make changes, the easy way and the hard way. Either way you will need to [create a GitHub account](https://github.com/signup/free).\n\nEasy way GitHub allows in-line editing of files for making simple typo changes and quick-fixes. This is not the best way as you are unable to test the code works. If you do this you could be introducing syntax errors, etc, but for a Git-phobic user this is good for a quick-fix.\n\nHard way The best way to contribute is to \"clone\" your fork of CodeIgniter to your development area. That sounds like some jargon, but \"forking\" on GitHub means \"making a copy of that repo to your account\" and \"cloning\" means \"copying that code to your environment so you can work on it\".\n\n1. [Set up Git](https://help.github.com/en/articles/set-up-git) (Windows, Mac & Linux)\n2. Go to the [CodeIgniter repo](https://github.com/bcit-ci/CodeIgniter)\n3. [Fork it](https://help.github.com/en/articles/fork-a-repo)\n4. [Clone](https://help.github.com/en/articles/fetching-a-remote#clone) your forked CodeIgniter repo: git@github.com:<your-name>/CodeIgniter.git.\n5. Checkout the \"develop\" branch. At this point you are ready to start making changes.\n6. Fix existing bugs on the Issue tracker after taking a look to see nobody else is working on them.\n7. [Commit](https://help.github.com/en/articles/adding-a-file-to-a-repository-using-the-command-line) the files\n8. [Push](https://help.github.com/en/articles/pushing-to-a-remote) your develop branch to your fork\n9. [Send a pull request](https://help.github.com/en/articles/creating-a-pull-request)\n\nThe Reactor Engineers will now be alerted about the change and at least one of the team will respond. If your change fails to meet the guidelines it will be bounced, or feedback will be provided to help you improve it.\n\nOnce the Reactor Engineer handling your pull request is happy with it they will merge it into develop and your patch will be part of the next release.\n\n### Keeping your fork up-to-date\n\nUnlike systems like Subversion, Git can have multiple remotes. A remote is the name for a URL of a Git repository. By default your fork will have a remote named \"origin\" which points to your fork, but you can add another remote named \"codeigniter\" which points to `git://github.com/bcit-ci/CodeIgniter.git`. This is a read-only remote but you can pull from this develop branch to update your own.\n\nIf you are using command-line you can do the following:\n\n1. `git remote add codeigniter git://github.com/bcit-ci/CodeIgniter.git`\n2. `git pull codeigniter develop`\n3. `git push origin develop`\n\nNow your fork is up to date. This should be done regularly, or before you send a pull request at least.\n"
  },
  {
    "path": "index.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2014 - 2019, British Columbia Institute of Technology\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\n\n/*\n *---------------------------------------------------------------\n * APPLICATION ENVIRONMENT\n *---------------------------------------------------------------\n *\n * You can load different configurations depending on your\n * current environment. Setting the environment also influences\n * things like logging and error reporting.\n *\n * This can be set to anything, but default usage is:\n *\n *     development\n *     testing\n *     production\n *\n * NOTE: If you change these, also change the error_reporting() code below\n */\n\tdefine('ENVIRONMENT', isset($_SERVER['CI_ENV']) ? $_SERVER['CI_ENV'] : 'development');\n\n/*\n *---------------------------------------------------------------\n * ERROR REPORTING\n *---------------------------------------------------------------\n *\n * Different environments will require different levels of error reporting.\n * By default development will show errors but testing and live will hide them.\n */\nswitch (ENVIRONMENT)\n{\n\tcase 'development':\n\t\terror_reporting(-1);\n\t\tini_set('display_errors', 1);\n\tbreak;\n\n\tcase 'testing':\n\tcase 'production':\n\t\tini_set('display_errors', 0);\n\t\terror_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT & ~E_USER_NOTICE & ~E_USER_DEPRECATED);\n\tbreak;\n\n\tdefault:\n\t\theader('HTTP/1.1 503 Service Unavailable.', TRUE, 503);\n\t\techo 'The application environment is not set correctly.';\n\t\texit(1); // EXIT_ERROR\n}\n\n/*\n *---------------------------------------------------------------\n * SYSTEM DIRECTORY NAME\n *---------------------------------------------------------------\n *\n * This variable must contain the name of your \"system\" directory.\n * Set the path if it is not in the same directory as this file.\n */\n\t$system_path = 'system';\n\n/*\n *---------------------------------------------------------------\n * APPLICATION DIRECTORY NAME\n *---------------------------------------------------------------\n *\n * If you want this front controller to use a different \"application\"\n * directory than the default one you can set its name here. The directory\n * can also be renamed or relocated anywhere on your server. If you do,\n * use an absolute (full) server path.\n * For more info please see the user guide:\n *\n * https://codeigniter.com/userguide3/general/managing_apps.html\n *\n * NO TRAILING SLASH!\n */\n\t$application_folder = 'application';\n\n/*\n *---------------------------------------------------------------\n * VIEW DIRECTORY NAME\n *---------------------------------------------------------------\n *\n * If you want to move the view directory out of the application\n * directory, set the path to it here. The directory can be renamed\n * and relocated anywhere on your server. If blank, it will default\n * to the standard location inside your application directory.\n * If you do move this, use an absolute (full) server path.\n *\n * NO TRAILING SLASH!\n */\n\t$view_folder = '';\n\n\n/*\n * --------------------------------------------------------------------\n * DEFAULT CONTROLLER\n * --------------------------------------------------------------------\n *\n * Normally you will set your default controller in the routes.php file.\n * You can, however, force a custom routing by hard-coding a\n * specific controller class/function here. For most applications, you\n * WILL NOT set your routing here, but it's an option for those\n * special instances where you might want to override the standard\n * routing in a specific front controller that shares a common CI installation.\n *\n * IMPORTANT: If you set the routing here, NO OTHER controller will be\n * callable. In essence, this preference limits your application to ONE\n * specific controller. Leave the function name blank if you need\n * to call functions dynamically via the URI.\n *\n * Un-comment the $routing array below to use this feature\n */\n\t// The directory name, relative to the \"controllers\" directory.  Leave blank\n\t// if your controller is not in a sub-directory within the \"controllers\" one\n\t// $routing['directory'] = '';\n\n\t// The controller class file name.  Example:  mycontroller\n\t// $routing['controller'] = '';\n\n\t// The controller function you wish to be called.\n\t// $routing['function']\t= '';\n\n\n/*\n * -------------------------------------------------------------------\n *  CUSTOM CONFIG VALUES\n * -------------------------------------------------------------------\n *\n * The $assign_to_config array below will be passed dynamically to the\n * config class when initialized. This allows you to set custom config\n * items or override any default config values found in the config.php file.\n * This can be handy as it permits you to share one application between\n * multiple front controller files, with each file containing different\n * config values.\n *\n * Un-comment the $assign_to_config array below to use this feature\n */\n\t// $assign_to_config['name_of_config_item'] = 'value of config item';\n\n\n\n// --------------------------------------------------------------------\n// END OF USER CONFIGURABLE SETTINGS.  DO NOT EDIT BELOW THIS LINE\n// --------------------------------------------------------------------\n\n/*\n * ---------------------------------------------------------------\n *  Resolve the system path for increased reliability\n * ---------------------------------------------------------------\n */\n\n\t// Set the current directory correctly for CLI requests\n\tif (defined('STDIN'))\n\t{\n\t\tchdir(dirname(__FILE__));\n\t}\n\n\tif (($_temp = realpath($system_path)) !== FALSE)\n\t{\n\t\t$system_path = $_temp.DIRECTORY_SEPARATOR;\n\t}\n\telse\n\t{\n\t\t// Ensure there's a trailing slash\n\t\t$system_path = strtr(\n\t\t\trtrim($system_path, '/\\\\'),\n\t\t\t'/\\\\',\n\t\t\tDIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR\n\t\t).DIRECTORY_SEPARATOR;\n\t}\n\n\t// Is the system path correct?\n\tif ( ! is_dir($system_path))\n\t{\n\t\theader('HTTP/1.1 503 Service Unavailable.', TRUE, 503);\n\t\techo 'Your system folder path does not appear to be set correctly. Please open the following file and correct this: '.pathinfo(__FILE__, PATHINFO_BASENAME);\n\t\texit(3); // EXIT_CONFIG\n\t}\n\n/*\n * -------------------------------------------------------------------\n *  Now that we know the path, set the main path constants\n * -------------------------------------------------------------------\n */\n\t// The name of THIS file\n\tdefine('SELF', pathinfo(__FILE__, PATHINFO_BASENAME));\n\n\t// Path to the system directory\n\tdefine('BASEPATH', $system_path);\n\n\t// Path to the front controller (this file) directory\n\tdefine('FCPATH', dirname(__FILE__).DIRECTORY_SEPARATOR);\n\n\t// Name of the \"system\" directory\n\tdefine('SYSDIR', basename(BASEPATH));\n\n\t// The path to the \"application\" directory\n\tif (is_dir($application_folder))\n\t{\n\t\tif (($_temp = realpath($application_folder)) !== FALSE)\n\t\t{\n\t\t\t$application_folder = $_temp;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$application_folder = strtr(\n\t\t\t\trtrim($application_folder, '/\\\\'),\n\t\t\t\t'/\\\\',\n\t\t\t\tDIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR\n\t\t\t);\n\t\t}\n\t}\n\telseif (is_dir(BASEPATH.$application_folder.DIRECTORY_SEPARATOR))\n\t{\n\t\t$application_folder = BASEPATH.strtr(\n\t\t\ttrim($application_folder, '/\\\\'),\n\t\t\t'/\\\\',\n\t\t\tDIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR\n\t\t);\n\t}\n\telse\n\t{\n\t\theader('HTTP/1.1 503 Service Unavailable.', TRUE, 503);\n\t\techo 'Your application folder path does not appear to be set correctly. Please open the following file and correct this: '.SELF;\n\t\texit(3); // EXIT_CONFIG\n\t}\n\n\tdefine('APPPATH', $application_folder.DIRECTORY_SEPARATOR);\n\n\t// The path to the \"views\" directory\n\tif ( ! isset($view_folder[0]) && is_dir(APPPATH.'views'.DIRECTORY_SEPARATOR))\n\t{\n\t\t$view_folder = APPPATH.'views';\n\t}\n\telseif (is_dir($view_folder))\n\t{\n\t\tif (($_temp = realpath($view_folder)) !== FALSE)\n\t\t{\n\t\t\t$view_folder = $_temp;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$view_folder = strtr(\n\t\t\t\trtrim($view_folder, '/\\\\'),\n\t\t\t\t'/\\\\',\n\t\t\t\tDIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR\n\t\t\t);\n\t\t}\n\t}\n\telseif (is_dir(APPPATH.$view_folder.DIRECTORY_SEPARATOR))\n\t{\n\t\t$view_folder = APPPATH.strtr(\n\t\t\ttrim($view_folder, '/\\\\'),\n\t\t\t'/\\\\',\n\t\t\tDIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR\n\t\t);\n\t}\n\telse\n\t{\n\t\theader('HTTP/1.1 503 Service Unavailable.', TRUE, 503);\n\t\techo 'Your view folder path does not appear to be set correctly. Please open the following file and correct this: '.SELF;\n\t\texit(3); // EXIT_CONFIG\n\t}\n\n\tdefine('VIEWPATH', $view_folder.DIRECTORY_SEPARATOR);\n\n/*\n * --------------------------------------------------------------------\n * LOAD THE BOOTSTRAP FILE\n * --------------------------------------------------------------------\n *\n * And away we go...\n */\nrequire_once BASEPATH.'core/CodeIgniter.php';\n"
  },
  {
    "path": "license.txt",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2019 - 2022, CodeIgniter Foundation\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "phpdoc.dist.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<phpdoc>\n\t<title>CodeIgniter v3.0.0 API</title>\n    <parser>\n        <target>./api/</target>\n    </parser>\n    <transformer>\n        <target>./api/</target>\n    </transformer>\n    <files>\n        <directory>./system</directory>\n    </files>\n\n    <logging>\n        <level>warn</level>\n        <paths>\n            <default>./api/log/{DATE}.log</default>\n            <errors>./api/{DATE}.errors.log</errors>\n        </paths>\n    </logging>\n</phpdoc>"
  },
  {
    "path": "readme.rst",
    "content": "###################\nWhat is CodeIgniter\n###################\n\nCodeIgniter is an Application Development Framework - a toolkit - for people\nwho build web sites using PHP. Its goal is to enable you to develop projects\nmuch faster than you could if you were writing code from scratch, by providing\na rich set of libraries for commonly needed tasks, as well as a simple\ninterface and logical structure to access these libraries. CodeIgniter lets\nyou creatively focus on your project by minimizing the amount of code needed\nfor a given task.\n\n*************\nCodeIgniter 3\n*************\n\nThis repository is for the legacy version, CodeIgniter 3.\n`CodeIgniter 4 <https://github.com/codeigniter4/CodeIgniter4>`_ is the latest\nversion of the framework.\n\nCodeIgniter 3 is the legacy version of the framework, intended for use with PHP\n5.6+. This version is in maintenance, receiving mostly just security updates.\n\n*******************\nRelease Information\n*******************\n\nThis repo contains in-development code for future releases. To download the\nlatest stable release please visit the `CodeIgniter Downloads\n<https://codeigniter.com/download>`_ page.\n\n**************************\nChangelog and New Features\n**************************\n\nYou can find a list of all changes for each release in the `user\nguide change log <https://github.com/bcit-ci/CodeIgniter/blob/develop/user_guide_src/source/changelog.rst>`_.\n\n*******************\nServer Requirements\n*******************\n\nPHP version 5.6 or newer is recommended.\n\nIt should work on 5.4.8 as well, but we strongly advise you NOT to run\nsuch old versions of PHP, because of potential security and performance\nissues, as well as missing features.\n\n************\nInstallation\n************\n\nPlease see the `installation section <https://codeigniter.com/userguide3/installation/index.html>`_\nof the CodeIgniter User Guide.\n\n*******\nLicense\n*******\n\nPlease see the `license\nagreement <https://github.com/bcit-ci/CodeIgniter/blob/develop/user_guide_src/source/license.rst>`_.\n\n*********\nResources\n*********\n\n-  `User Guide <https://codeigniter.com/userguide3/>`_\n-  `Contributing Guide <https://github.com/bcit-ci/CodeIgniter/blob/develop/contributing.md>`_\n-  `Language File Translations <https://github.com/bcit-ci/codeigniter3-translations>`_\n-  `Community Forums <https://forum.codeigniter.com/>`_\n-  `Community Wiki <https://github.com/bcit-ci/CodeIgniter/wiki>`_\n-  `Community Slack Channel <https://codeigniterchat.slack.com>`_\n\nReport security issues to our `Security Panel <mailto:security@codeigniter.com>`_\nor via our `page on HackerOne <https://hackerone.com/codeigniter>`_, thank you.\n\n***************\nAcknowledgement\n***************\n\nThe CodeIgniter team would like to thank EllisLab, all the\ncontributors to the CodeIgniter project and you, the CodeIgniter user.\n"
  },
  {
    "path": "system/.htaccess",
    "content": "<IfModule authz_core_module>\n\tRequire all denied\n</IfModule>\n<IfModule !authz_core_module>\n\tDeny from all\n</IfModule>"
  },
  {
    "path": "system/core/Benchmark.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Benchmark Class\n *\n * This class enables you to mark points and calculate the time difference\n * between them. Memory consumption can also be displayed.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tLibraries\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/benchmark.html\n */\nclass CI_Benchmark {\n\n\t/**\n\t * List of all benchmark markers\n\t *\n\t * @var\tarray\n\t */\n\tpublic $marker = array();\n\n\t/**\n\t * Set a benchmark marker\n\t *\n\t * Multiple calls to this function can be made so that several\n\t * execution points can be timed.\n\t *\n\t * @param\tstring\t$name\tMarker name\n\t * @return\tvoid\n\t */\n\tpublic function mark($name)\n\t{\n\t\t$this->marker[$name] = microtime(TRUE);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Elapsed time\n\t *\n\t * Calculates the time difference between two marked points.\n\t *\n\t * If the first parameter is empty this function instead returns the\n\t * {elapsed_time} pseudo-variable. This permits the full system\n\t * execution time to be shown in a template. The output class will\n\t * swap the real value for this variable.\n\t *\n\t * @param\tstring\t$point1\t\tA particular marked point\n\t * @param\tstring\t$point2\t\tA particular marked point\n\t * @param\tint\t$decimals\tNumber of decimal places\n\t *\n\t * @return\tstring\tCalculated elapsed time on success,\n\t *\t\t\tan '{elapsed_string}' if $point1 is empty\n\t *\t\t\tor an empty string if $point1 is not found.\n\t */\n\tpublic function elapsed_time($point1 = '', $point2 = '', $decimals = 4)\n\t{\n\t\tif ($point1 === '')\n\t\t{\n\t\t\treturn '{elapsed_time}';\n\t\t}\n\n\t\tif ( ! isset($this->marker[$point1]))\n\t\t{\n\t\t\treturn '';\n\t\t}\n\n\t\tif ( ! isset($this->marker[$point2]))\n\t\t{\n\t\t\t$this->marker[$point2] = microtime(TRUE);\n\t\t}\n\n\t\treturn number_format($this->marker[$point2] - $this->marker[$point1], $decimals);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Memory Usage\n\t *\n\t * Simply returns the {memory_usage} marker.\n\t *\n\t * This permits it to be put it anywhere in a template\n\t * without the memory being calculated until the end.\n\t * The output class will swap the real value for this variable.\n\t *\n\t * @return\tstring\t'{memory_usage}'\n\t */\n\tpublic function memory_usage()\n\t{\n\t\treturn '{memory_usage}';\n\t}\n\n}\n"
  },
  {
    "path": "system/core/CodeIgniter.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * System Initialization File\n *\n * Loads the base classes and executes the request.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tCodeIgniter\n * @category\tFront-controller\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/\n */\n\n/**\n * CodeIgniter Version\n *\n * @var\tstring\n *\n */\n\tconst CI_VERSION = '3.2.0-dev';\n\n/*\n * ------------------------------------------------------\n *  Load the framework constants\n * ------------------------------------------------------\n */\n\tif (file_exists(APPPATH.'config/'.ENVIRONMENT.'/constants.php'))\n\t{\n\t\trequire_once(APPPATH.'config/'.ENVIRONMENT.'/constants.php');\n\t}\n\n\tif (file_exists(APPPATH.'config/constants.php'))\n\t{\n\t\trequire_once(APPPATH.'config/constants.php');\n\t}\n\n/*\n * ------------------------------------------------------\n *  Load the global functions\n * ------------------------------------------------------\n */\n\trequire_once(BASEPATH.'core/Common.php');\n\n/*\n * ------------------------------------------------------\n *  Define a custom error handler so we can log PHP errors\n * ------------------------------------------------------\n */\n\tset_error_handler('_error_handler');\n\tset_exception_handler('_exception_handler');\n\tregister_shutdown_function('_shutdown_handler');\n\n/*\n * ------------------------------------------------------\n *  Set the subclass_prefix\n * ------------------------------------------------------\n *\n * Normally the \"subclass_prefix\" is set in the config file.\n * The subclass prefix allows CI to know if a core class is\n * being extended via a library in the local application\n * \"libraries\" folder. Since CI allows config items to be\n * overridden via data set in the main index.php file,\n * before proceeding we need to know if a subclass_prefix\n * override exists. If so, we will set this value now,\n * before any classes are loaded\n * Note: Since the config file data is cached it doesn't\n * hurt to load it here.\n */\n\tif ( ! empty($assign_to_config['subclass_prefix']))\n\t{\n\t\tget_config(array('subclass_prefix' => $assign_to_config['subclass_prefix']));\n\t}\n\n/*\n * ------------------------------------------------------\n *  Should we use a Composer autoloader?\n * ------------------------------------------------------\n */\n\tif ($composer_autoload = config_item('composer_autoload'))\n\t{\n\t\tif ($composer_autoload === TRUE)\n\t\t{\n\t\t\tfile_exists(APPPATH.'vendor/autoload.php')\n\t\t\t\t? require_once(APPPATH.'vendor/autoload.php')\n\t\t\t\t: log_message('error', '$config[\\'composer_autoload\\'] is set to TRUE but '.APPPATH.'vendor/autoload.php was not found.');\n\t\t}\n\t\telseif (file_exists($composer_autoload))\n\t\t{\n\t\t\trequire_once($composer_autoload);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlog_message('error', 'Could not find the specified $config[\\'composer_autoload\\'] path: '.$composer_autoload);\n\t\t}\n\t}\n\n/*\n * ------------------------------------------------------\n *  Start the timer... tick tock tick tock...\n * ------------------------------------------------------\n */\n\t$BM =& load_class('Benchmark', 'core');\n\t$BM->mark('total_execution_time_start');\n\t$BM->mark('loading_time:_base_classes_start');\n\n/*\n * ------------------------------------------------------\n *  Instantiate the config class\n * ------------------------------------------------------\n *\n * Note: It is important that Config is loaded first as\n * most other classes depend on it either directly or by\n * depending on another class that uses it.\n *\n */\n\t$CFG =& load_class('Config', 'core');\n\n\t// Do we have any manually set config items in the index.php file?\n\tif (isset($assign_to_config) && is_array($assign_to_config))\n\t{\n\t\tforeach ($assign_to_config as $key => $value)\n\t\t{\n\t\t\t$CFG->set_item($key, $value);\n\t\t}\n\t}\n\n/*\n * ------------------------------------------------------\n *  Instantiate the hooks class\n * ------------------------------------------------------\n */\n\t$EXT =& load_class('Hooks', 'core', $CFG);\n\n/*\n * ------------------------------------------------------\n *  Is there a \"pre_system\" hook?\n * ------------------------------------------------------\n */\n\t$EXT->call_hook('pre_system');\n\n/*\n * ------------------------------------------------------\n * Important charset-related stuff\n * ------------------------------------------------------\n *\n * Configure mbstring and/or iconv if they are enabled\n * and set MB_ENABLED and ICONV_ENABLED constants, so\n * that we don't repeatedly do extension_loaded() or\n * function_exists() calls.\n *\n * Note: UTF-8 class depends on this. It used to be done\n * in it's constructor, but it's _not_ class-specific.\n *\n */\n\t$charset = strtoupper(config_item('charset'));\n\tini_set('default_charset', $charset);\n\n\tif (extension_loaded('mbstring'))\n\t{\n\t\tdefine('MB_ENABLED', TRUE);\n\t\t// mbstring.internal_encoding is deprecated starting with PHP 5.6\n\t\t// and it's usage triggers E_DEPRECATED messages.\n\t\t@ini_set('mbstring.internal_encoding', $charset);\n\t\t// This is required for mb_convert_encoding() to strip invalid characters.\n\t\t// That's utilized by CI_Utf8, but it's also done for consistency with iconv.\n\t\tmb_substitute_character('none');\n\t}\n\telse\n\t{\n\t\tdefine('MB_ENABLED', FALSE);\n\t}\n\n\t// There's an ICONV_IMPL constant, but the PHP manual says that using\n\t// iconv's predefined constants is \"strongly discouraged\".\n\tif (extension_loaded('iconv'))\n\t{\n\t\tdefine('ICONV_ENABLED', TRUE);\n\t\t// iconv.internal_encoding is deprecated starting with PHP 5.6\n\t\t// and it's usage triggers E_DEPRECATED messages.\n\t\t@ini_set('iconv.internal_encoding', $charset);\n\t}\n\telse\n\t{\n\t\tdefine('ICONV_ENABLED', FALSE);\n\t}\n\n\tif (is_php('5.6'))\n\t{\n\t\tini_set('php.internal_encoding', $charset);\n\t}\n\n/*\n * ------------------------------------------------------\n *  Load compatibility features\n * ------------------------------------------------------\n */\n\n\trequire_once(BASEPATH.'core/compat/mbstring.php');\n\trequire_once(BASEPATH.'core/compat/hash.php');\n\trequire_once(BASEPATH.'core/compat/password.php');\n\trequire_once(BASEPATH.'core/compat/standard.php');\n\n/*\n * ------------------------------------------------------\n *  Instantiate the UTF-8 class\n * ------------------------------------------------------\n */\n\t$UNI =& load_class('Utf8', 'core', $charset);\n\n/*\n * ------------------------------------------------------\n *  Instantiate the URI class\n * ------------------------------------------------------\n */\n\t$URI =& load_class('URI', 'core', $CFG);\n\n/*\n * ------------------------------------------------------\n *  Instantiate the routing class and set the routing\n * ------------------------------------------------------\n */\n\t$RTR =& load_class('Router', 'core', isset($routing) ? $routing : NULL);\n\n/*\n * ------------------------------------------------------\n *  Instantiate the output class\n * ------------------------------------------------------\n */\n\t$OUT =& load_class('Output', 'core');\n\n/*\n * ------------------------------------------------------\n *\tIs there a valid cache file? If so, we're done...\n * ------------------------------------------------------\n */\n\tif ($EXT->call_hook('cache_override') === FALSE && $OUT->_display_cache($CFG, $URI) === TRUE)\n\t{\n\t\texit;\n\t}\n\n/*\n * -----------------------------------------------------\n * Load the security class for xss and csrf support\n * -----------------------------------------------------\n */\n\t$SEC =& load_class('Security', 'core', $charset);\n\n/*\n * ------------------------------------------------------\n *  Load the Input class and sanitize globals\n * ------------------------------------------------------\n */\n\t$IN =& load_class('Input', 'core', $SEC);\n\n/*\n * ------------------------------------------------------\n *  Load the Language class\n * ------------------------------------------------------\n */\n\t$LANG =& load_class('Lang', 'core');\n\n/*\n * ------------------------------------------------------\n *  Load the app controller and local controller\n * ------------------------------------------------------\n *\n */\n\t// Load the base controller class\n\trequire_once BASEPATH.'core/Controller.php';\n\n\t/**\n\t * Reference to the CI_Controller method.\n\t *\n\t * Returns current CI instance object\n\t *\n\t * @return CI_Controller\n\t */\n\tfunction &get_instance()\n\t{\n\t\treturn CI_Controller::get_instance();\n\t}\n\n\tif (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'))\n\t{\n\t\trequire_once APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php';\n\t}\n\n\t// Set a mark point for benchmarking\n\t$BM->mark('loading_time:_base_classes_end');\n\n/*\n * ------------------------------------------------------\n *  Sanity checks\n * ------------------------------------------------------\n *\n *  The Router class has already validated the request,\n *  leaving us with 3 options here:\n *\n *\t1) an empty class name, if we reached the default\n *\t   controller, but it didn't exist;\n *\t2) a query string which doesn't go through a\n *\t   file_exists() check\n *\t3) a regular request for a non-existing page\n *\n *  We handle all of these as a 404 error.\n *\n *  Furthermore, none of the methods in the app controller\n *  or the loader class can be called via the URI, nor can\n *  controller methods that begin with an underscore.\n */\n\n\t$e404 = FALSE;\n\t$class = ucfirst($RTR->class);\n\t$method = $RTR->method;\n\n\tif (empty($class) OR ! file_exists(APPPATH.'controllers/'.$RTR->directory.$class.'.php'))\n\t{\n\t\t$e404 = TRUE;\n\t}\n\telse\n\t{\n\t\trequire_once(APPPATH.'controllers/'.$RTR->directory.$class.'.php');\n\n\t\tif ( ! class_exists($class, FALSE) OR $method[0] === '_' OR method_exists('CI_Controller', $method))\n\t\t{\n\t\t\t$e404 = TRUE;\n\t\t}\n\t\telseif (method_exists($class, '_remap'))\n\t\t{\n\t\t\t$params = array($method, array_slice($URI->rsegments, 2));\n\t\t\t$method = '_remap';\n\t\t}\n\t\telseif ( ! method_exists($class, $method))\n\t\t{\n\t\t\t$e404 = TRUE;\n\t\t}\n\t\t/**\n\t\t * DO NOT CHANGE THIS, NOTHING ELSE WORKS!\n\t\t *\n\t\t * - method_exists() returns true for non-public methods, which passes the previous elseif\n\t\t * - is_callable() returns false for PHP 4-style constructors, even if there's a __construct()\n\t\t * - method_exists($class, '__construct') won't work because CI_Controller::__construct() is inherited\n\t\t * - People will only complain if this doesn't work, even though it is documented that it shouldn't.\n\t\t *\n\t\t * ReflectionMethod::isConstructor() is the ONLY reliable check,\n\t\t * knowing which method will be executed as a constructor.\n\t\t */\n\t\telse\n\t\t{\n\t\t\t$reflection = new ReflectionMethod($class, $method);\n\t\t\tif ( ! $reflection->isPublic() OR $reflection->isConstructor())\n\t\t\t{\n\t\t\t\t$e404 = TRUE;\n\t\t\t}\n\t\t}\n\t}\n\n\tif ($e404)\n\t{\n\t\tif ( ! empty($RTR->routes['404_override']))\n\t\t{\n\t\t\tif (sscanf($RTR->routes['404_override'], '%[^/]/%s', $error_class, $error_method) !== 2)\n\t\t\t{\n\t\t\t\t$error_method = 'index';\n\t\t\t}\n\n\t\t\t$error_class = ucfirst($error_class);\n\n\t\t\tif ( ! class_exists($error_class, FALSE))\n\t\t\t{\n\t\t\t\tif (file_exists(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php'))\n\t\t\t\t{\n\t\t\t\t\trequire_once(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php');\n\t\t\t\t\t$e404 = ! class_exists($error_class, FALSE);\n\t\t\t\t}\n\t\t\t\t// Were we in a directory? If so, check for a global override\n\t\t\t\telseif ( ! empty($RTR->directory) && file_exists(APPPATH.'controllers/'.$error_class.'.php'))\n\t\t\t\t{\n\t\t\t\t\trequire_once(APPPATH.'controllers/'.$error_class.'.php');\n\t\t\t\t\tif (($e404 = ! class_exists($error_class, FALSE)) === FALSE)\n\t\t\t\t\t{\n\t\t\t\t\t\t$RTR->directory = '';\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$e404 = FALSE;\n\t\t\t}\n\t\t}\n\n\t\t// Did we reset the $e404 flag? If so, set the rsegments, starting from index 1\n\t\tif ( ! $e404)\n\t\t{\n\t\t\t$class = $error_class;\n\t\t\t$method = $error_method;\n\n\t\t\t$URI->rsegments = array(\n\t\t\t\t1 => $class,\n\t\t\t\t2 => $method\n\t\t\t);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tshow_404($RTR->directory.$class.'/'.$method);\n\t\t}\n\t}\n\n\tif ($method !== '_remap')\n\t{\n\t\t$params = array_slice($URI->rsegments, 2);\n\t}\n\n/*\n * ------------------------------------------------------\n *  Is there a \"pre_controller\" hook?\n * ------------------------------------------------------\n */\n\t$EXT->call_hook('pre_controller');\n\n/*\n * ------------------------------------------------------\n *  Instantiate the requested controller\n * ------------------------------------------------------\n */\n\t// Mark a start point so we can benchmark the controller\n\t$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_start');\n\n\t$CI = new $class();\n\n/*\n * ------------------------------------------------------\n *  Is there a \"post_controller_constructor\" hook?\n * ------------------------------------------------------\n */\n\t$EXT->call_hook('post_controller_constructor');\n\n/*\n * ------------------------------------------------------\n *  Call the requested method\n * ------------------------------------------------------\n */\n\tcall_user_func_array(array(&$CI, $method), $params);\n\n\t// Mark a benchmark end point\n\t$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end');\n\n/*\n * ------------------------------------------------------\n *  Is there a \"post_controller\" hook?\n * ------------------------------------------------------\n */\n\t$EXT->call_hook('post_controller');\n\n/*\n * ------------------------------------------------------\n *  Send the final rendered output to the browser\n * ------------------------------------------------------\n */\n\tif ($EXT->call_hook('display_override') === FALSE)\n\t{\n\t\t$OUT->_display();\n\t}\n\n/*\n * ------------------------------------------------------\n *  Is there a \"post_system\" hook?\n * ------------------------------------------------------\n */\n\t$EXT->call_hook('post_system');\n"
  },
  {
    "path": "system/core/Common.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Common Functions\n *\n * Loads the base classes and executes the request.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tCodeIgniter\n * @category\tCommon Functions\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/\n */\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('is_php'))\n{\n\t/**\n\t * Determines if the current version of PHP is equal to or greater than the supplied value\n\t *\n\t * @param\tstring\n\t * @return\tbool\tTRUE if the current version is $version or higher\n\t */\n\tfunction is_php($version)\n\t{\n\t\tstatic $_is_php;\n\t\t$version = (string) $version;\n\n\t\tif ( ! isset($_is_php[$version]))\n\t\t{\n\t\t\t$_is_php[$version] = version_compare(PHP_VERSION, $version, '>=');\n\t\t}\n\n\t\treturn $_is_php[$version];\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('is_really_writable'))\n{\n\t/**\n\t * Tests for file writability\n\t *\n\t * is_writable() returns TRUE on Windows servers when you really can't write to\n\t * the file, based on the read-only attribute.\n\t *\n\t * @link\thttps://bugs.php.net/bug.php?id=54709\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tfunction is_really_writable($file)\n\t{\n\t\t// If we're on a UNIX-like server, just is_writable()\n\t\tif (DIRECTORY_SEPARATOR === '/')\n\t\t{\n\t\t\treturn is_writable($file);\n\t\t}\n\n\t\t/* For Windows servers and safe_mode \"on\" installations we'll actually\n\t\t * write a file then read it. Bah...\n\t\t */\n\t\tif (is_dir($file))\n\t\t{\n\t\t\t$file = rtrim($file, '/').'/'.md5(mt_rand());\n\t\t\tif (($fp = @fopen($file, 'ab')) === FALSE)\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\tfclose($fp);\n\t\t\t@chmod($file, 0777);\n\t\t\t@unlink($file);\n\t\t\treturn TRUE;\n\t\t}\n\t\telseif ( ! is_file($file) OR ($fp = @fopen($file, 'ab')) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tfclose($fp);\n\t\treturn TRUE;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('load_class'))\n{\n\t/**\n\t * Class registry\n\t *\n\t * This function acts as a singleton. If the requested class does not\n\t * exist it is instantiated and set to a static variable. If it has\n\t * previously been instantiated the variable is returned.\n\t *\n\t * @param\tstring\tthe class name being requested\n\t * @param\tstring\tthe directory where the class should be found\n\t * @param\tmixed\tan optional argument to pass to the class constructor\n\t * @return\tobject\n\t */\n\tfunction &load_class($class, $directory = 'libraries', $param = NULL)\n\t{\n\t\tstatic $_classes = array();\n\n\t\t// Does the class exist? If so, we're done...\n\t\tif (isset($_classes[$class]))\n\t\t{\n\t\t\treturn $_classes[$class];\n\t\t}\n\n\t\t$name = FALSE;\n\n\t\t// Look for the class first in the local application/libraries folder\n\t\t// then in the native system/libraries folder\n\t\tforeach (array(APPPATH, BASEPATH) as $path)\n\t\t{\n\t\t\tif (file_exists($path.$directory.'/'.$class.'.php'))\n\t\t\t{\n\t\t\t\t$name = 'CI_'.$class;\n\n\t\t\t\tif (class_exists($name, FALSE) === FALSE)\n\t\t\t\t{\n\t\t\t\t\trequire_once($path.$directory.'/'.$class.'.php');\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Is the request a class extension? If so we load it too\n\t\tif (file_exists(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php'))\n\t\t{\n\t\t\t$name = config_item('subclass_prefix').$class;\n\n\t\t\tif (class_exists($name, FALSE) === FALSE)\n\t\t\t{\n\t\t\t\trequire_once(APPPATH.$directory.'/'.$name.'.php');\n\t\t\t}\n\t\t}\n\n\t\t// Did we find the class?\n\t\tif ($name === FALSE)\n\t\t{\n\t\t\t// Note: We use exit() rather than show_error() in order to avoid a\n\t\t\t// self-referencing loop with the Exceptions class\n\t\t\tset_status_header(503);\n\t\t\techo 'Unable to locate the specified class: '.$class.'.php';\n\t\t\texit(5); // EXIT_UNK_CLASS\n\t\t}\n\n\t\t// Keep track of what we just loaded\n\t\tis_loaded($class);\n\n\t\t$_classes[$class] = isset($param)\n\t\t\t? new $name($param)\n\t\t\t: new $name();\n\t\treturn $_classes[$class];\n\t}\n}\n\n// --------------------------------------------------------------------\n\nif ( ! function_exists('is_loaded'))\n{\n\t/**\n\t * Keeps track of which libraries have been loaded. This function is\n\t * called by the load_class() function above\n\t *\n\t * @param\tstring\n\t * @return\tarray\n\t */\n\tfunction &is_loaded($class = '')\n\t{\n\t\tstatic $_is_loaded = array();\n\n\t\tif ($class !== '')\n\t\t{\n\t\t\t$_is_loaded[strtolower($class)] = $class;\n\t\t}\n\n\t\treturn $_is_loaded;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('get_config'))\n{\n\t/**\n\t * Loads the main config.php file\n\t *\n\t * This function lets us grab the config file even if the Config class\n\t * hasn't been instantiated yet\n\t *\n\t * @param\tarray\n\t * @return\tarray\n\t */\n\tfunction &get_config(Array $replace = array())\n\t{\n\t\tstatic $config;\n\n\t\tif (empty($config))\n\t\t{\n\t\t\t$file_path = APPPATH.'config/config.php';\n\t\t\t$found = FALSE;\n\t\t\tif (file_exists($file_path))\n\t\t\t{\n\t\t\t\t$found = TRUE;\n\t\t\t\trequire($file_path);\n\t\t\t}\n\n\t\t\t// Is the config file in the environment folder?\n\t\t\tif (file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/config.php'))\n\t\t\t{\n\t\t\t\trequire($file_path);\n\t\t\t}\n\t\t\telseif ( ! $found)\n\t\t\t{\n\t\t\t\tset_status_header(503);\n\t\t\t\techo 'The configuration file does not exist.';\n\t\t\t\texit(3); // EXIT_CONFIG\n\t\t\t}\n\n\t\t\t// Does the $config array exist in the file?\n\t\t\tif ( ! isset($config) OR ! is_array($config))\n\t\t\t{\n\t\t\t\tset_status_header(503);\n\t\t\t\techo 'Your config file does not appear to be formatted correctly.';\n\t\t\t\texit(3); // EXIT_CONFIG\n\t\t\t}\n\t\t}\n\n\t\t// Are any values being dynamically added or replaced?\n\t\tforeach ($replace as $key => $val)\n\t\t{\n\t\t\t$config[$key] = $val;\n\t\t}\n\n\t\treturn $config;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('config_item'))\n{\n\t/**\n\t * Returns the specified config item\n\t *\n\t * @param\tstring\n\t * @return\tmixed\n\t */\n\tfunction config_item($item)\n\t{\n\t\tstatic $_config;\n\n\t\tif (empty($_config))\n\t\t{\n\t\t\t// references cannot be directly assigned to static variables, so we use an array\n\t\t\t$_config[0] =& get_config();\n\t\t}\n\n\t\treturn isset($_config[0][$item]) ? $_config[0][$item] : NULL;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('get_mimes'))\n{\n\t/**\n\t * Returns the MIME types array from config/mimes.php\n\t *\n\t * @return\tarray\n\t */\n\tfunction &get_mimes()\n\t{\n\t\tstatic $_mimes;\n\n\t\tif (empty($_mimes))\n\t\t{\n\t\t\t$_mimes = file_exists(APPPATH.'config/mimes.php')\n\t\t\t\t? include(APPPATH.'config/mimes.php')\n\t\t\t\t: array();\n\n\t\t\tif (file_exists(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'))\n\t\t\t{\n\t\t\t\t$_mimes = array_merge($_mimes, include(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'));\n\t\t\t}\n\t\t}\n\n\t\treturn $_mimes;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('is_https'))\n{\n\t/**\n\t * Is HTTPS?\n\t *\n\t * Determines if the application is accessed via an encrypted\n\t * (HTTPS) connection.\n\t *\n\t * @return\tbool\n\t */\n\tfunction is_https()\n\t{\n\t\tif ( ! empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off')\n\t\t{\n\t\t\treturn TRUE;\n\t\t}\n\t\telseif (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https')\n\t\t{\n\t\t\treturn TRUE;\n\t\t}\n\t\telseif ( ! empty($_SERVER['HTTP_FRONT_END_HTTPS']) && strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) !== 'off')\n\t\t{\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('is_cli'))\n{\n\n\t/**\n\t * Is CLI?\n\t *\n\t * Test to see if a request was made from the command line.\n\t *\n\t * @return \tbool\n\t */\n\tfunction is_cli()\n\t{\n\t\treturn (PHP_SAPI === 'cli' OR defined('STDIN'));\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('show_error'))\n{\n\t/**\n\t * Error Handler\n\t *\n\t * This function lets us invoke the exception class and\n\t * display errors using the standard error template located\n\t * in application/views/errors/error_general.php\n\t * This function will send the error page directly to the\n\t * browser and exit.\n\t *\n\t * @param\tstring\n\t * @param\tint\n\t * @param\tstring\n\t * @return\tvoid\n\t */\n\tfunction show_error($message, $status_code = 500, $heading = 'An Error Was Encountered')\n\t{\n\t\t$status_code = abs($status_code);\n\t\tif ($status_code < 100)\n\t\t{\n\t\t\t$exit_status = $status_code + 9; // 9 is EXIT__AUTO_MIN\n\t\t\t$status_code = 500;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$exit_status = 1; // EXIT_ERROR\n\t\t}\n\n\t\t$_error =& load_class('Exceptions', 'core');\n\t\techo $_error->show_error($heading, $message, 'error_general', $status_code);\n\t\texit($exit_status);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('show_404'))\n{\n\t/**\n\t * 404 Page Handler\n\t *\n\t * This function is similar to the show_error() function above\n\t * However, instead of the standard error template it displays\n\t * 404 errors.\n\t *\n\t * @param\tstring\n\t * @param\tbool\n\t * @return\tvoid\n\t */\n\tfunction show_404($page = '', $log_error = TRUE)\n\t{\n\t\t$_error =& load_class('Exceptions', 'core');\n\t\t$_error->show_404($page, $log_error);\n\t\texit(4); // EXIT_UNKNOWN_FILE\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('log_message'))\n{\n\t/**\n\t * Error Logging Interface\n\t *\n\t * We use this as a simple mechanism to access the logging\n\t * class and send messages to be logged.\n\t *\n\t * @param\tstring\tthe error level: 'error', 'debug' or 'info'\n\t * @param\tstring\tthe error message\n\t * @return\tvoid\n\t */\n\tfunction log_message($level, $message)\n\t{\n\t\tstatic $_log;\n\n\t\tif ($_log === NULL)\n\t\t{\n\t\t\t// references cannot be directly assigned to static variables, so we use an array\n\t\t\t$_log[0] =& load_class('Log', 'core');\n\t\t}\n\n\t\t$_log[0]->write_log($level, $message);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('set_status_header'))\n{\n\t/**\n\t * Set HTTP Status Header\n\t *\n\t * @param\tint\tthe status code\n\t * @param\tstring\n\t * @return\tvoid\n\t */\n\tfunction set_status_header($code = 200, $text = '')\n\t{\n\t\tif (is_cli())\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tif (empty($code) OR ! is_numeric($code))\n\t\t{\n\t\t\tshow_error('Status codes must be numeric', 500);\n\t\t}\n\n\t\tif (empty($text))\n\t\t{\n\t\t\tis_int($code) OR $code = (int) $code;\n\t\t\t$stati = array(\n\t\t\t\t100\t=> 'Continue',\n\t\t\t\t101\t=> 'Switching Protocols',\n\t\t\t\t103\t=> 'Early Hints',\n\n\t\t\t\t200\t=> 'OK',\n\t\t\t\t201\t=> 'Created',\n\t\t\t\t202\t=> 'Accepted',\n\t\t\t\t203\t=> 'Non-Authoritative Information',\n\t\t\t\t204\t=> 'No Content',\n\t\t\t\t205\t=> 'Reset Content',\n\t\t\t\t206\t=> 'Partial Content',\n\t\t\t\t207\t=> 'Multi-Status',\n\n\t\t\t\t300\t=> 'Multiple Choices',\n\t\t\t\t301\t=> 'Moved Permanently',\n\t\t\t\t302\t=> 'Found',\n\t\t\t\t303\t=> 'See Other',\n\t\t\t\t304\t=> 'Not Modified',\n\t\t\t\t305\t=> 'Use Proxy',\n\t\t\t\t307\t=> 'Temporary Redirect',\n\t\t\t\t308\t=> 'Permanent Redirect',\n\n\t\t\t\t400\t=> 'Bad Request',\n\t\t\t\t401\t=> 'Unauthorized',\n\t\t\t\t402\t=> 'Payment Required',\n\t\t\t\t403\t=> 'Forbidden',\n\t\t\t\t404\t=> 'Not Found',\n\t\t\t\t405\t=> 'Method Not Allowed',\n\t\t\t\t406\t=> 'Not Acceptable',\n\t\t\t\t407\t=> 'Proxy Authentication Required',\n\t\t\t\t408\t=> 'Request Timeout',\n\t\t\t\t409\t=> 'Conflict',\n\t\t\t\t410\t=> 'Gone',\n\t\t\t\t411\t=> 'Length Required',\n\t\t\t\t412\t=> 'Precondition Failed',\n\t\t\t\t413\t=> 'Request Entity Too Large',\n\t\t\t\t414\t=> 'Request-URI Too Long',\n\t\t\t\t415\t=> 'Unsupported Media Type',\n\t\t\t\t416\t=> 'Requested Range Not Satisfiable',\n\t\t\t\t417\t=> 'Expectation Failed',\n\t\t\t\t421\t=> 'Misdirected Request',\n\t\t\t\t422\t=> 'Unprocessable Entity',\n\t\t\t\t426\t=> 'Upgrade Required',\n\t\t\t\t428\t=> 'Precondition Required',\n\t\t\t\t429\t=> 'Too Many Requests',\n\t\t\t\t431\t=> 'Request Header Fields Too Large',\n\t\t\t\t451\t=> 'Unavailable For Legal Reasons',\n\n\t\t\t\t500\t=> 'Internal Server Error',\n\t\t\t\t501\t=> 'Not Implemented',\n\t\t\t\t502\t=> 'Bad Gateway',\n\t\t\t\t503\t=> 'Service Unavailable',\n\t\t\t\t504\t=> 'Gateway Timeout',\n\t\t\t\t505\t=> 'HTTP Version Not Supported',\n\t\t\t\t511\t=> 'Network Authentication Required',\n\t\t\t);\n\n\t\t\tif (isset($stati[$code]))\n\t\t\t{\n\t\t\t\t$text = $stati[$code];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tshow_error('No status text available. Please check your status code number or supply your own message text.', 500);\n\t\t\t}\n\t\t}\n\n\t\tif (strpos(PHP_SAPI, 'cgi') === 0)\n\t\t{\n\t\t\theader('Status: '.$code.' '.$text, TRUE);\n\t\t\treturn;\n\t\t}\n\n\t\t$server_protocol = (isset($_SERVER['SERVER_PROTOCOL']) && in_array($_SERVER['SERVER_PROTOCOL'], array('HTTP/1.0', 'HTTP/1.1', 'HTTP/2', 'HTTP/2.0'), TRUE))\n\t\t\t? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1';\n\t\theader($server_protocol.' '.$code.' '.$text, TRUE, $code);\n\t}\n}\n\n// --------------------------------------------------------------------\n\nif ( ! function_exists('_error_handler'))\n{\n\t/**\n\t * Error Handler\n\t *\n\t * This is the custom error handler that is declared at the (relative)\n\t * top of CodeIgniter.php. The main reason we use this is to permit\n\t * PHP errors to be logged in our own log files since the user may\n\t * not have access to server logs. Since this function effectively\n\t * intercepts PHP errors, however, we also need to display errors\n\t * based on the current error_reporting level.\n\t * We do that with the use of a PHP error template.\n\t *\n\t * @param\tint\t$severity\n\t * @param\tstring\t$message\n\t * @param\tstring\t$filepath\n\t * @param\tint\t$line\n\t * @return\tvoid\n\t */\n\tfunction _error_handler($severity, $message, $filepath, $line)\n\t{\n\t\t$is_error = (((E_ERROR | E_PARSE | E_COMPILE_ERROR | E_CORE_ERROR | E_USER_ERROR) & $severity) === $severity);\n\n\t\t// When an error occurred, set the status header to '500 Internal Server Error'\n\t\t// to indicate to the client something went wrong.\n\t\t// This can't be done within the $_error->show_php_error method because\n\t\t// it is only called when the display_errors flag is set (which isn't usually\n\t\t// the case in a production environment) or when errors are ignored because\n\t\t// they are above the error_reporting threshold.\n\t\tif ($is_error)\n\t\t{\n\t\t\tset_status_header(500);\n\t\t}\n\n\t\t// Should we ignore the error? We'll get the current error_reporting\n\t\t// level and add its bits with the severity bits to find out.\n\t\tif (($severity & error_reporting()) !== $severity)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t$_error =& load_class('Exceptions', 'core');\n\t\t$_error->log_exception($severity, $message, $filepath, $line);\n\n\t\t// Should we display the error?\n\t\tif (str_ireplace(array('off', 'none', 'no', 'false', 'null'), '', ini_get('display_errors')))\n\t\t{\n\t\t\t$_error->show_php_error($severity, $message, $filepath, $line);\n\t\t}\n\n\t\t// If the error is fatal, the execution of the script should be stopped because\n\t\t// errors can't be recovered from. Halting the script conforms with PHP's\n\t\t// default error handling. See https://secure.php.net/manual/en/errorfunc.constants.php\n\t\tif ($is_error)\n\t\t{\n\t\t\texit(1); // EXIT_ERROR\n\t\t}\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('_exception_handler'))\n{\n\t/**\n\t * Exception Handler\n\t *\n\t * Sends uncaught exceptions to the logger and displays them\n\t * only if display_errors is On so that they don't show up in\n\t * production environments.\n\t *\n\t * @param\tException\t$exception\n\t * @return\tvoid\n\t */\n\tfunction _exception_handler($exception)\n\t{\n\t\t$_error =& load_class('Exceptions', 'core');\n\t\t$_error->log_exception('error', 'Exception: '.$exception->getMessage(), $exception->getFile(), $exception->getLine());\n\n\t\tis_cli() OR set_status_header(500);\n\t\t// Should we display the error?\n\t\tif (str_ireplace(array('off', 'none', 'no', 'false', 'null'), '', ini_get('display_errors')))\n\t\t{\n\t\t\t$_error->show_exception($exception);\n\t\t}\n\n\t\texit(1); // EXIT_ERROR\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('_shutdown_handler'))\n{\n\t/**\n\t * Shutdown Handler\n\t *\n\t * This is the shutdown handler that is declared at the top\n\t * of CodeIgniter.php. The main reason we use this is to simulate\n\t * a complete custom exception handler.\n\t *\n\t * E_STRICT is purposively neglected because such events may have\n\t * been caught. Duplication or none? None is preferred for now.\n\t *\n\t * @link\thttp://insomanic.me.uk/post/229851073/php-trick-catching-fatal-errors-e-error-with-a\n\t * @return\tvoid\n\t */\n\tfunction _shutdown_handler()\n\t{\n\t\t$last_error = error_get_last();\n\t\tif (isset($last_error) &&\n\t\t\t($last_error['type'] & (E_ERROR | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING)))\n\t\t{\n\t\t\t_error_handler($last_error['type'], $last_error['message'], $last_error['file'], $last_error['line']);\n\t\t}\n\t}\n}\n\n// --------------------------------------------------------------------\n\nif ( ! function_exists('remove_invisible_characters'))\n{\n\t/**\n\t * Remove Invisible Characters\n\t *\n\t * This prevents sandwiching null characters\n\t * between ascii characters, like Java\\0script.\n\t *\n\t * @param\tstring\n\t * @param\tbool\n\t * @return\tstring\n\t */\n\tfunction remove_invisible_characters($str, $url_encoded = TRUE)\n\t{\n\t\t$non_displayables = array();\n\n\t\t// every control character except newline (dec 10),\n\t\t// carriage return (dec 13) and horizontal tab (dec 09)\n\t\tif ($url_encoded)\n\t\t{\n\t\t\t$non_displayables[] = '/%0[0-8bcef]/i';\t// url encoded 00-08, 11, 12, 14, 15\n\t\t\t$non_displayables[] = '/%1[0-9a-f]/i';\t// url encoded 16-31\n\t\t\t$non_displayables[] = '/%7f/i';\t// url encoded 127\n\t\t}\n\n\t\t$non_displayables[] = '/[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]+/S';\t// 00-08, 11, 12, 14-31, 127\n\n\t\tdo\n\t\t{\n\t\t\t$str = preg_replace($non_displayables, '', $str, -1, $count);\n\t\t}\n\t\twhile ($count);\n\n\t\treturn $str;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('html_escape'))\n{\n\t/**\n\t * Returns HTML escaped variable.\n\t *\n\t * @param\tmixed\t$var\t\tThe input string or array of strings to be escaped.\n\t * @param\tbool\t$double_encode\t$double_encode set to FALSE prevents escaping twice.\n\t * @return\tmixed\t\t\tThe escaped string or array of strings as a result.\n\t */\n\tfunction html_escape($var, $double_encode = TRUE)\n\t{\n\t\tif (empty($var))\n\t\t{\n\t\t\treturn $var;\n\t\t}\n\n\t\tif (is_array($var))\n\t\t{\n\t\t\tforeach (array_keys($var) as $key)\n\t\t\t{\n\t\t\t\t$var[$key] = html_escape($var[$key], $double_encode);\n\t\t\t}\n\n\t\t\treturn $var;\n\t\t}\n\n\t\treturn htmlspecialchars($var, ENT_QUOTES, config_item('charset'), $double_encode);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('_stringify_attributes'))\n{\n\t/**\n\t * Stringify attributes for use in HTML tags.\n\t *\n\t * Helper function used to convert a string, array, or object\n\t * of attributes to a string.\n\t *\n\t * @param\tmixed\tstring, array, object\n\t * @param\tbool\n\t * @return\tstring\n\t */\n\tfunction _stringify_attributes($attributes, $js = FALSE)\n\t{\n\t\tif (empty($attributes))\n\t\t{\n\t\t\treturn NULL;\n\t\t}\n\n\t\tif (is_string($attributes))\n\t\t{\n\t\t\treturn ' '.$attributes;\n\t\t}\n\n\t\t$attributes = (array) $attributes;\n\n\t\t$atts = '';\n\t\tforeach ($attributes as $key => $val)\n\t\t{\n\t\t\t$atts .= ($js) ? $key.'='.$val.',' : ' '.$key.'=\"'.$val.'\"';\n\t\t}\n\n\t\treturn rtrim($atts, ',');\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('function_usable'))\n{\n\t/**\n\t * Function usable\n\t *\n\t * Executes a function_exists() check, and if the Suhosin PHP\n\t * extension is loaded - checks whether the function that is\n\t * checked might be disabled in there as well.\n\t *\n\t * This is useful as function_exists() will return FALSE for\n\t * functions disabled via the *disable_functions* php.ini\n\t * setting, but not for *suhosin.executor.func.blacklist* and\n\t * *suhosin.executor.disable_eval*. These settings will just\n\t * terminate script execution if a disabled function is executed.\n\t *\n\t * The above described behavior turned out to be a bug in Suhosin,\n\t * but even though a fix was committed for 0.9.34 on 2012-02-12,\n\t * that version is yet to be released. This function will therefore\n\t * be just temporary, but would probably be kept for a few years.\n\t *\n\t * @link\thttp://www.hardened-php.net/suhosin/\n\t * @param\tstring\t$function_name\tFunction to check for\n\t * @return\tbool\tTRUE if the function exists and is safe to call,\n\t *\t\t\tFALSE otherwise.\n\t */\n\tfunction function_usable($function_name)\n\t{\n\t\tstatic $_suhosin_func_blacklist;\n\n\t\tif (function_exists($function_name))\n\t\t{\n\t\t\tif ( ! isset($_suhosin_func_blacklist))\n\t\t\t{\n\t\t\t\t$_suhosin_func_blacklist = extension_loaded('suhosin')\n\t\t\t\t\t? explode(',', trim(ini_get('suhosin.executor.func.blacklist')))\n\t\t\t\t\t: array();\n\t\t\t}\n\n\t\t\treturn ! in_array($function_name, $_suhosin_func_blacklist, TRUE);\n\t\t}\n\n\t\treturn FALSE;\n\t}\n}\n"
  },
  {
    "path": "system/core/Config.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Config Class\n *\n * This class contains functions that enable config files to be managed\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tLibraries\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/config.html\n */\nclass CI_Config {\n\n\t/**\n\t * List of all loaded config values\n\t *\n\t * @var\tarray\n\t */\n\tpublic $config = array();\n\n\t/**\n\t * List of all loaded config files\n\t *\n\t * @var\tarray\n\t */\n\tpublic $is_loaded =\tarray();\n\n\t/**\n\t * List of paths to search when trying to load a config file.\n\t *\n\t * @used-by\tCI_Loader\n\t * @var\t\tarray\n\t */\n\tpublic $_config_paths =\tarray(APPPATH);\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * Sets the $config data from the primary config.php file as a class variable.\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __construct()\n\t{\n\t\t$this->config =& get_config();\n\n\t\t// Set the base_url automatically if none was provided\n\t\tif (empty($this->config['base_url']))\n\t\t{\n\t\t\tif (isset($_SERVER['SERVER_ADDR']))\n\t\t\t{\n\t\t\t\tif (strpos($_SERVER['SERVER_ADDR'], ':') !== FALSE)\n\t\t\t\t{\n\t\t\t\t\t$server_addr = '['.$_SERVER['SERVER_ADDR'].']';\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$server_addr = $_SERVER['SERVER_ADDR'];\n\t\t\t\t}\n\n\t\t\t\t$base_url = (is_https() ? 'https' : 'http').'://'.$server_addr\n\t\t\t\t\t.substr($_SERVER['SCRIPT_NAME'], 0, strpos($_SERVER['SCRIPT_NAME'], basename($_SERVER['SCRIPT_FILENAME'])));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$base_url = 'http://localhost/';\n\t\t\t}\n\n\t\t\t$this->set_item('base_url', $base_url);\n\t\t}\n\n\t\tlog_message('info', 'Config Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Load Config File\n\t *\n\t * @param\tstring\t$file\t\t\tConfiguration file name\n\t * @param\tbool\t$use_sections\t\tWhether configuration values should be loaded into their own section\n\t * @param\tbool\t$fail_gracefully\tWhether to just return FALSE or display an error message\n\t * @return\tbool\tTRUE if the file was loaded correctly or FALSE on failure\n\t */\n\tpublic function load($file = '', $use_sections = FALSE, $fail_gracefully = FALSE)\n\t{\n\t\t$file = ($file === '') ? 'config' : str_replace('.php', '', $file);\n\t\t$loaded = FALSE;\n\n\t\tforeach ($this->_config_paths as $path)\n\t\t{\n\t\t\tforeach (array($file, ENVIRONMENT.DIRECTORY_SEPARATOR.$file) as $location)\n\t\t\t{\n\t\t\t\t$file_path = $path.'config/'.$location.'.php';\n\t\t\t\tif (in_array($file_path, $this->is_loaded, TRUE))\n\t\t\t\t{\n\t\t\t\t\treturn TRUE;\n\t\t\t\t}\n\n\t\t\t\tif ( ! file_exists($file_path))\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tinclude($file_path);\n\n\t\t\t\tif ( ! isset($config) OR ! is_array($config))\n\t\t\t\t{\n\t\t\t\t\tif ($fail_gracefully === TRUE)\n\t\t\t\t\t{\n\t\t\t\t\t\treturn FALSE;\n\t\t\t\t\t}\n\n\t\t\t\t\tshow_error('Your '.$file_path.' file does not appear to contain a valid configuration array.');\n\t\t\t\t}\n\n\t\t\t\tif ($use_sections === TRUE)\n\t\t\t\t{\n\t\t\t\t\t$this->config[$file] = isset($this->config[$file])\n\t\t\t\t\t\t? array_merge($this->config[$file], $config)\n\t\t\t\t\t\t: $config;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$this->config = array_merge($this->config, $config);\n\t\t\t\t}\n\n\t\t\t\t$this->is_loaded[] = $file_path;\n\t\t\t\t$config = NULL;\n\t\t\t\t$loaded = TRUE;\n\t\t\t\tlog_message('info', 'Config file loaded: '.$file_path);\n\t\t\t}\n\t\t}\n\n\t\tif ($loaded === TRUE)\n\t\t{\n\t\t\treturn TRUE;\n\t\t}\n\t\telseif ($fail_gracefully === TRUE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tshow_error('The configuration file '.$file.'.php does not exist.');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch a config file item\n\t *\n\t * @param\tstring\t$item\tConfig item name\n\t * @param\tstring\t$index\tIndex name\n\t * @return\tstring|null\tThe configuration item or NULL if the item doesn't exist\n\t */\n\tpublic function item($item, $index = '')\n\t{\n\t\tif ($index == '')\n\t\t{\n\t\t\treturn isset($this->config[$item]) ? $this->config[$item] : NULL;\n\t\t}\n\n\t\treturn isset($this->config[$index], $this->config[$index][$item]) ? $this->config[$index][$item] : NULL;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch a config file item with slash appended (if not empty)\n\t *\n\t * @param\tstring\t\t$item\tConfig item name\n\t * @return\tstring|null\tThe configuration item or NULL if the item doesn't exist\n\t */\n\tpublic function slash_item($item)\n\t{\n\t\tif ( ! isset($this->config[$item]))\n\t\t{\n\t\t\treturn NULL;\n\t\t}\n\t\telseif (trim($this->config[$item]) === '')\n\t\t{\n\t\t\treturn '';\n\t\t}\n\n\t\treturn rtrim($this->config[$item], '/').'/';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Site URL\n\t *\n\t * Returns base_url . index_page [. uri_string]\n\t *\n\t * @uses\tCI_Config::_uri_string()\n\t *\n\t * @param\tstring|string[]\t$uri\tURI string or an array of segments\n\t * @param\tstring\t$protocol\n\t * @return\tstring\n\t */\n\tpublic function site_url($uri = '', $protocol = NULL)\n\t{\n\t\t$base_url = $this->slash_item('base_url');\n\n\t\tif (isset($protocol))\n\t\t{\n\t\t\t// For protocol-relative links\n\t\t\tif ($protocol === '')\n\t\t\t{\n\t\t\t\t$base_url = substr($base_url, strpos($base_url, '//'));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$base_url = $protocol.substr($base_url, strpos($base_url, '://'));\n\t\t\t}\n\t\t}\n\n\t\tif (empty($uri))\n\t\t{\n\t\t\treturn $base_url.$this->item('index_page');\n\t\t}\n\n\t\t$uri = $this->_uri_string($uri);\n\n\t\tif ($this->item('enable_query_strings') === FALSE)\n\t\t{\n\t\t\t$suffix = isset($this->config['url_suffix']) ? $this->config['url_suffix'] : '';\n\n\t\t\tif ($suffix !== '')\n\t\t\t{\n\t\t\t\tif (($offset = strpos($uri, '?')) !== FALSE)\n\t\t\t\t{\n\t\t\t\t\t$uri = substr($uri, 0, $offset).$suffix.substr($uri, $offset);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$uri .= $suffix;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn $base_url.$this->slash_item('index_page').$uri;\n\t\t}\n\t\telseif (strpos($uri, '?') === FALSE)\n\t\t{\n\t\t\t$uri = '?'.$uri;\n\t\t}\n\n\t\treturn $base_url.$this->item('index_page').$uri;\n\t}\n\n\t// -------------------------------------------------------------\n\n\t/**\n\t * Base URL\n\t *\n\t * Returns base_url [. uri_string]\n\t *\n\t * @uses\tCI_Config::_uri_string()\n\t *\n\t * @param\tstring|string[]\t$uri\tURI string or an array of segments\n\t * @param\tstring\t$protocol\n\t * @return\tstring\n\t */\n\tpublic function base_url($uri = '', $protocol = NULL)\n\t{\n\t\t$base_url = $this->slash_item('base_url');\n\n\t\tif (isset($protocol))\n\t\t{\n\t\t\t// For protocol-relative links\n\t\t\tif ($protocol === '')\n\t\t\t{\n\t\t\t\t$base_url = substr($base_url, strpos($base_url, '//'));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$base_url = $protocol.substr($base_url, strpos($base_url, '://'));\n\t\t\t}\n\t\t}\n\n\t\treturn $base_url.$this->_uri_string($uri);\n\t}\n\n\t// -------------------------------------------------------------\n\n\t/**\n\t * Build URI string\n\t *\n\t * @used-by\tCI_Config::site_url()\n\t * @used-by\tCI_Config::base_url()\n\t *\n\t * @param\tstring|string[]\t$uri\tURI string or an array of segments\n\t * @return\tstring\n\t */\n\tprotected function _uri_string($uri)\n\t{\n\t\tif ($this->item('enable_query_strings') === FALSE)\n\t\t{\n\t\t\tis_array($uri) && $uri = implode('/', $uri);\n\t\t\treturn ltrim($uri, '/');\n\t\t}\n\t\telseif (is_array($uri))\n\t\t{\n\t\t\treturn http_build_query($uri);\n\t\t}\n\n\t\treturn $uri;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set a config file item\n\t *\n\t * @param\tstring\t$item\tConfig item key\n\t * @param\tstring\t$value\tConfig item value\n\t * @return\tvoid\n\t */\n\tpublic function set_item($item, $value)\n\t{\n\t\t$this->config[$item] = $value;\n\t}\n\n}\n"
  },
  {
    "path": "system/core/Controller.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Application Controller Class\n *\n * This class object is the super class that every library in\n * CodeIgniter will be assigned to.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tLibraries\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/general/controllers.html\n */\nclass CI_Controller {\n\n\t/**\n\t * Reference to the CI singleton\n\t *\n\t * @var\tobject\n\t */\n\tprivate static $instance;\n\n\t/**\n\t * CI_Loader\n\t *\n\t * @var\tCI_Loader\n\t */\n\tpublic $load;\n\n\t/**\n\t * Class constructor\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __construct()\n\t{\n\t\tself::$instance =& $this;\n\n\t\t// Assign all the class objects that were instantiated by the\n\t\t// bootstrap file (CodeIgniter.php) to local class variables\n\t\t// so that CI can run as one big super object.\n\t\tforeach (is_loaded() as $var => $class)\n\t\t{\n\t\t\t$this->$var =& load_class($class);\n\t\t}\n\n\t\t$this->load =& load_class('Loader', 'core');\n\t\t$this->load->initialize();\n\t\tlog_message('info', 'Controller Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get the CI singleton\n\t *\n\t * @static\n\t * @return\tobject\n\t */\n\tpublic static function &get_instance()\n\t{\n\t\treturn self::$instance;\n\t}\n\n}\n"
  },
  {
    "path": "system/core/Exceptions.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Exceptions Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tExceptions\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/exceptions.html\n */\nclass CI_Exceptions {\n\n\t/**\n\t * Nesting level of the output buffering mechanism\n\t *\n\t * @var\tint\n\t */\n\tpublic $ob_level;\n\n\t/**\n\t * List of available error levels\n\t *\n\t * @var\tarray\n\t */\n\tpublic $levels = array(\n\t\tE_ERROR\t\t\t=>\t'Error',\n\t\tE_WARNING\t\t=>\t'Warning',\n\t\tE_PARSE\t\t\t=>\t'Parsing Error',\n\t\tE_NOTICE\t\t=>\t'Notice',\n\t\tE_CORE_ERROR\t\t=>\t'Core Error',\n\t\tE_CORE_WARNING\t\t=>\t'Core Warning',\n\t\tE_COMPILE_ERROR\t\t=>\t'Compile Error',\n\t\tE_COMPILE_WARNING\t=>\t'Compile Warning',\n\t\tE_USER_ERROR\t\t=>\t'User Error',\n\t\tE_USER_WARNING\t\t=>\t'User Warning',\n\t\tE_USER_NOTICE\t\t=>\t'User Notice',\n\t\tE_STRICT\t\t=>\t'Runtime Notice'\n\t);\n\n\t/**\n\t * Class constructor\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __construct()\n\t{\n\t\t$this->ob_level = ob_get_level();\n\t\t// Note: Do not log messages from this constructor.\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Exception Logger\n\t *\n\t * Logs PHP generated error messages\n\t *\n\t * @param\tint\t$severity\tLog level\n\t * @param\tstring\t$message\tError message\n\t * @param\tstring\t$filepath\tFile path\n\t * @param\tint\t$line\t\tLine number\n\t * @return\tvoid\n\t */\n\tpublic function log_exception($severity, $message, $filepath, $line)\n\t{\n\t\t$severity = isset($this->levels[$severity]) ? $this->levels[$severity] : $severity;\n\t\tlog_message('error', 'Severity: '.$severity.' --> '.$message.' '.$filepath.' '.$line);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * 404 Error Handler\n\t *\n\t * @uses\tCI_Exceptions::show_error()\n\t *\n\t * @param\tstring\t$page\t\tPage URI\n\t * @param \tbool\t$log_error\tWhether to log the error\n\t * @return\tvoid\n\t */\n\tpublic function show_404($page = '', $log_error = TRUE)\n\t{\n\t\tif (is_cli())\n\t\t{\n\t\t\t$heading = 'Not Found';\n\t\t\t$message = 'The controller/method pair you requested was not found.';\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$heading = '404 Page Not Found';\n\t\t\t$message = 'The page you requested was not found.';\n\t\t}\n\n\t\t// By default we log this, but allow a dev to skip it\n\t\tif ($log_error)\n\t\t{\n\t\t\tlog_message('error', $heading.': '.$page);\n\t\t}\n\n\t\techo $this->show_error($heading, $message, 'error_404', 404);\n\t\texit(4); // EXIT_UNKNOWN_FILE\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * General Error Page\n\t *\n\t * Takes an error message as input (either as a string or an array)\n\t * and displays it using the specified template.\n\t *\n\t * @param\tstring\t\t$heading\tPage heading\n\t * @param\tstring|string[]\t$message\tError message\n\t * @param\tstring\t\t$template\tTemplate name\n\t * @param \tint\t\t$status_code\t(default: 500)\n\t *\n\t * @return\tstring\tError page output\n\t */\n\tpublic function show_error($heading, $message, $template = 'error_general', $status_code = 500)\n\t{\n\t\t$templates_path = config_item('error_views_path');\n\t\tif (empty($templates_path))\n\t\t{\n\t\t\t$templates_path = VIEWPATH.'errors'.DIRECTORY_SEPARATOR;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$templates_path = rtrim($templates_path, '/\\\\').DIRECTORY_SEPARATOR;\n\t\t}\n\n\t\tif (is_cli())\n\t\t{\n\t\t\t$message = \"\\t\".(is_array($message) ? implode(\"\\n\\t\", $message) : $message);\n\t\t\t$template = 'cli'.DIRECTORY_SEPARATOR.$template;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tset_status_header($status_code);\n\t\t\t$message = '<p>'.(is_array($message) ? implode('</p><p>', $message) : $message).'</p>';\n\t\t\t$template = 'html'.DIRECTORY_SEPARATOR.$template;\n\t\t}\n\n\t\tif (ob_get_level() > $this->ob_level + 1)\n\t\t{\n\t\t\tob_end_flush();\n\t\t}\n\t\tob_start();\n\t\tinclude($templates_path.$template.'.php');\n\t\t$buffer = ob_get_contents();\n\t\tob_end_clean();\n\t\treturn $buffer;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function show_exception($exception)\n\t{\n\t\t$templates_path = config_item('error_views_path');\n\t\tif (empty($templates_path))\n\t\t{\n\t\t\t$templates_path = VIEWPATH.'errors'.DIRECTORY_SEPARATOR;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$templates_path = rtrim($templates_path, '/\\\\').DIRECTORY_SEPARATOR;\n\t\t}\n\n\t\t$message = $exception->getMessage();\n\t\tif (empty($message))\n\t\t{\n\t\t\t$message = '(null)';\n\t\t}\n\n\t\tif (is_cli())\n\t\t{\n\t\t\t$templates_path .= 'cli'.DIRECTORY_SEPARATOR;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$templates_path .= 'html'.DIRECTORY_SEPARATOR;\n\t\t}\n\n\t\tif (ob_get_level() > $this->ob_level + 1)\n\t\t{\n\t\t\tob_end_flush();\n\t\t}\n\n\t\tob_start();\n\t\tinclude($templates_path.'error_exception.php');\n\t\t$buffer = ob_get_contents();\n\t\tob_end_clean();\n\t\techo $buffer;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Native PHP error handler\n\t *\n\t * @param\tint\t$severity\tError level\n\t * @param\tstring\t$message\tError message\n\t * @param\tstring\t$filepath\tFile path\n\t * @param\tint\t$line\t\tLine number\n\t * @return\tvoid\n\t */\n\tpublic function show_php_error($severity, $message, $filepath, $line)\n\t{\n\t\t$templates_path = config_item('error_views_path');\n\t\tif (empty($templates_path))\n\t\t{\n\t\t\t$templates_path = VIEWPATH.'errors'.DIRECTORY_SEPARATOR;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$templates_path = rtrim($templates_path, '/\\\\').DIRECTORY_SEPARATOR;\n\t\t}\n\n\t\t$severity = isset($this->levels[$severity]) ? $this->levels[$severity] : $severity;\n\n\t\t// For safety reasons we don't show the full file path in non-CLI requests\n\t\tif ( ! is_cli())\n\t\t{\n\t\t\t$filepath = str_replace('\\\\', '/', $filepath);\n\t\t\tif (FALSE !== strpos($filepath, '/'))\n\t\t\t{\n\t\t\t\t$x = explode('/', $filepath);\n\t\t\t\t$filepath = $x[count($x)-2].'/'.end($x);\n\t\t\t}\n\n\t\t\t$template = 'html'.DIRECTORY_SEPARATOR.'error_php';\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$template = 'cli'.DIRECTORY_SEPARATOR.'error_php';\n\t\t}\n\n\t\tif (ob_get_level() > $this->ob_level + 1)\n\t\t{\n\t\t\tob_end_flush();\n\t\t}\n\t\tob_start();\n\t\tinclude($templates_path.$template.'.php');\n\t\t$buffer = ob_get_contents();\n\t\tob_end_clean();\n\t\techo $buffer;\n\t}\n\n}\n"
  },
  {
    "path": "system/core/Hooks.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Hooks Class\n *\n * Provides a mechanism to extend the base system without hacking.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tLibraries\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/general/hooks.html\n */\nclass CI_Hooks {\n\n\t/**\n\t * Determines whether hooks are enabled\n\t *\n\t * @var\tbool\n\t */\n\tpublic $enabled = FALSE;\n\n\t/**\n\t * List of all hooks set in config/hooks.php\n\t *\n\t * @var\tarray\n\t */\n\tpublic $hooks =\tarray();\n\n\t/**\n\t * Array with class objects to use hooks methods\n\t *\n\t * @var array\n\t */\n\tprotected $_objects = array();\n\n\t/**\n\t * In progress flag\n\t *\n\t * Determines whether hook is in progress, used to prevent infinte loops\n\t *\n\t * @var\tbool\n\t */\n\tprotected $_in_progress = FALSE;\n\n\t/**\n\t * Class constructor\n\t *\n\t * @param\tCI_Config\t$config\n\t * @return\tvoid\n\t */\n\tpublic function __construct(CI_Config $config)\n\t{\n\t\tlog_message('info', 'Hooks Class Initialized');\n\n\t\t// If hooks are not enabled in the config file\n\t\t// there is nothing else to do\n\t\tif ($config->item('enable_hooks') === FALSE)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t// Grab the \"hooks\" definition file.\n\t\tif (file_exists(APPPATH.'config/hooks.php'))\n\t\t{\n\t\t\tinclude(APPPATH.'config/hooks.php');\n\t\t}\n\n\t\tif (file_exists(APPPATH.'config/'.ENVIRONMENT.'/hooks.php'))\n\t\t{\n\t\t\tinclude(APPPATH.'config/'.ENVIRONMENT.'/hooks.php');\n\t\t}\n\n\t\t// If there are no hooks, we're done.\n\t\tif ( ! isset($hook) OR ! is_array($hook))\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t$this->hooks =& $hook;\n\t\t$this->enabled = TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Call Hook\n\t *\n\t * Calls a particular hook. Called by CodeIgniter.php.\n\t *\n\t * @uses\tCI_Hooks::_run_hook()\n\t *\n\t * @param\tstring\t$which\tHook name\n\t * @return\tbool\tTRUE on success or FALSE on failure\n\t */\n\tpublic function call_hook($which = '')\n\t{\n\t\tif ( ! $this->enabled OR ! isset($this->hooks[$which]))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif (is_array($this->hooks[$which]) && ! isset($this->hooks[$which]['function']))\n\t\t{\n\t\t\tforeach ($this->hooks[$which] as $val)\n\t\t\t{\n\t\t\t\t$this->_run_hook($val);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$this->_run_hook($this->hooks[$which]);\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Run Hook\n\t *\n\t * Runs a particular hook\n\t *\n\t * @param\tarray\t$data\tHook details\n\t * @return\tbool\tTRUE on success or FALSE on failure\n\t */\n\tprotected function _run_hook($data)\n\t{\n\t\t// Closures/lambda functions and array($object, 'method') callables\n\t\tif (is_callable($data))\n\t\t{\n\t\t\tis_array($data)\n\t\t\t\t? $data[0]->{$data[1]}()\n\t\t\t\t: $data();\n\n\t\t\treturn TRUE;\n\t\t}\n\t\telseif ( ! is_array($data))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// -----------------------------------\n\t\t// Safety - Prevents run-away loops\n\t\t// -----------------------------------\n\n\t\t// If the script being called happens to have the same\n\t\t// hook call within it a loop can happen\n\t\tif ($this->_in_progress === TRUE)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t// -----------------------------------\n\t\t// Set file path\n\t\t// -----------------------------------\n\n\t\tif ( ! isset($data['filepath'], $data['filename']))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$filepath = APPPATH.$data['filepath'].'/'.$data['filename'];\n\n\t\tif ( ! file_exists($filepath))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Determine and class and/or function names\n\t\t$class\t\t= empty($data['class']) ? FALSE : $data['class'];\n\t\t$function\t= empty($data['function']) ? FALSE : $data['function'];\n\t\t$params\t\t= isset($data['params']) ? $data['params'] : '';\n\n\t\tif (empty($function))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Set the _in_progress flag\n\t\t$this->_in_progress = TRUE;\n\n\t\t// Call the requested class and/or function\n\t\tif ($class !== FALSE)\n\t\t{\n\t\t\t// The object is stored?\n\t\t\tif (isset($this->_objects[$class]))\n\t\t\t{\n\t\t\t\tif (method_exists($this->_objects[$class], $function))\n\t\t\t\t{\n\t\t\t\t\t$this->_objects[$class]->$function($params);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\treturn $this->_in_progress = FALSE;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tclass_exists($class, FALSE) OR require_once($filepath);\n\n\t\t\t\tif ( ! class_exists($class, FALSE) OR ! method_exists($class, $function))\n\t\t\t\t{\n\t\t\t\t\treturn $this->_in_progress = FALSE;\n\t\t\t\t}\n\n\t\t\t\t// Store the object and execute the method\n\t\t\t\t$this->_objects[$class] = new $class();\n\t\t\t\t$this->_objects[$class]->$function($params);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfunction_exists($function) OR require_once($filepath);\n\n\t\t\tif ( ! function_exists($function))\n\t\t\t{\n\t\t\t\treturn $this->_in_progress = FALSE;\n\t\t\t}\n\n\t\t\t$function($params);\n\t\t}\n\n\t\t$this->_in_progress = FALSE;\n\t\treturn TRUE;\n\t}\n\n}\n"
  },
  {
    "path": "system/core/Input.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Input Class\n *\n * Pre-processes global input data for security\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tInput\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/input.html\n */\nclass CI_Input {\n\n\t/**\n\t * IP address of the current user\n\t *\n\t * @var\tstring\n\t */\n\tprotected $ip_address = FALSE;\n\n\t/**\n\t * List of all HTTP request headers\n\t *\n\t * @var array\n\t */\n\tprotected $headers = array();\n\n\t/**\n\t * Raw input stream data\n\t *\n\t * Holds a cache of php://input contents\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_raw_input_stream;\n\n\t/**\n\t * Parsed input stream data\n\t *\n\t * Parsed from php://input at runtime\n\t *\n\t * @see\tCI_Input::input_stream()\n\t * @var\tarray\n\t */\n\tprotected $_input_stream;\n\n\t/**\n\t * CI_Security instance\n\t *\n\t * Used for the optional $xss_filter parameter that most\n\t * getter methods have here.\n\t *\n\t * @var\tCI_Security\n\t */\n\tprotected $security;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * Determines whether to globally enable the XSS processing\n\t * and whether to allow the $_GET array.\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __construct(CI_Security &$security)\n\t{\n\t\t$this->security = $security;\n\t\tlog_message('info', 'Input Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch from array\n\t *\n\t * Internal method used to retrieve values from global arrays.\n\t *\n\t * @param\tarray\t&$array\t\t$_GET, $_POST, $_COOKIE, $_SERVER, etc.\n\t * @param\tmixed\t$index\t\tIndex for item to be fetched from $array\n\t * @param\tbool\t$xss_clean\tWhether to apply XSS filtering\n\t * @return\tmixed\n\t */\n\tprotected function _fetch_from_array(&$array, $index = NULL, $xss_clean = FALSE)\n\t{\n\t\t// If $index is NULL, it means that the whole $array is requested\n\t\tisset($index) OR $index = array_keys($array);\n\n\t\t// allow fetching multiple keys at once\n\t\tif (is_array($index))\n\t\t{\n\t\t\t$output = array();\n\t\t\tforeach ($index as $key)\n\t\t\t{\n\t\t\t\t$output[$key] = $this->_fetch_from_array($array, $key, $xss_clean);\n\t\t\t}\n\n\t\t\treturn $output;\n\t\t}\n\n\t\tif (isset($array[$index]))\n\t\t{\n\t\t\t$value = $array[$index];\n\t\t}\n\t\telseif (($count = preg_match_all('/(?:^[^\\[]+)|\\[[^]]*\\]/', $index, $matches)) > 1) // Does the index contain array notation\n\t\t{\n\t\t\t$value = $array;\n\t\t\tfor ($i = 0; $i < $count; $i++)\n\t\t\t{\n\t\t\t\t$key = trim($matches[0][$i], '[]');\n\t\t\t\tif ($key === '') // Empty notation will return the value as array\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (isset($value[$key]))\n\t\t\t\t{\n\t\t\t\t\t$value = $value[$key];\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn NULL;\n\t\t}\n\n\t\treturn ($xss_clean === TRUE)\n\t\t\t? $this->security->xss_clean($value)\n\t\t\t: $value;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch an item from the GET array\n\t *\n\t * @param\tmixed\t$index\t\tIndex for item to be fetched from $_GET\n\t * @param\tbool\t$xss_clean\tWhether to apply XSS filtering\n\t * @return\tmixed\n\t */\n\tpublic function get($index = NULL, $xss_clean = FALSE)\n\t{\n\t\treturn $this->_fetch_from_array($_GET, $index, $xss_clean);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch an item from the POST array\n\t *\n\t * @param\tmixed\t$index\t\tIndex for item to be fetched from $_POST\n\t * @param\tbool\t$xss_clean\tWhether to apply XSS filtering\n\t * @return\tmixed\n\t */\n\tpublic function post($index = NULL, $xss_clean = FALSE)\n\t{\n\t\treturn $this->_fetch_from_array($_POST, $index, $xss_clean);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch an item from POST data with fallback to GET\n\t *\n\t * @param\tstring\t$index\t\tIndex for item to be fetched from $_POST or $_GET\n\t * @param\tbool\t$xss_clean\tWhether to apply XSS filtering\n\t * @return\tmixed\n\t */\n\tpublic function post_get($index, $xss_clean = FALSE)\n\t{\n\t\t$output = $this->post($index, $xss_clean);\n\t\treturn isset($output) ? $output : $this->get($index, $xss_clean);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch an item from GET data with fallback to POST\n\t *\n\t * @param\tstring\t$index\t\tIndex for item to be fetched from $_GET or $_POST\n\t * @param\tbool\t$xss_clean\tWhether to apply XSS filtering\n\t * @return\tmixed\n\t */\n\tpublic function get_post($index, $xss_clean = FALSE)\n\t{\n\t\t$output = $this->get($index, $xss_clean);\n\t\treturn isset($output) ? $output : $this->post($index, $xss_clean);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch an item from the COOKIE array\n\t *\n\t * @param\tmixed\t$index\t\tIndex for item to be fetched from $_COOKIE\n\t * @param\tbool\t$xss_clean\tWhether to apply XSS filtering\n\t * @return\tmixed\n\t */\n\tpublic function cookie($index = NULL, $xss_clean = FALSE)\n\t{\n\t\treturn $this->_fetch_from_array($_COOKIE, $index, $xss_clean);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch an item from the SERVER array\n\t *\n\t * @param\tmixed\t$index\t\tIndex for item to be fetched from $_SERVER\n\t * @param\tbool\t$xss_clean\tWhether to apply XSS filtering\n\t * @return\tmixed\n\t */\n\tpublic function server($index, $xss_clean = FALSE)\n\t{\n\t\treturn $this->_fetch_from_array($_SERVER, $index, $xss_clean);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Fetch an item from the php://input stream\n\t *\n\t * Useful when you need to access PUT, DELETE or PATCH request data.\n\t *\n\t * @param\tstring\t$index\t\tIndex for item to be fetched\n\t * @param\tbool\t$xss_clean\tWhether to apply XSS filtering\n\t * @return\tmixed\n\t */\n\tpublic function input_stream($index = NULL, $xss_clean = FALSE)\n\t{\n\t\t// Prior to PHP 5.6, the input stream can only be read once,\n\t\t// so we'll need to check if we have already done that first.\n\t\tif ( ! is_array($this->_input_stream))\n\t\t{\n\t\t\t// $this->raw_input_stream will trigger __get().\n\t\t\tparse_str($this->raw_input_stream, $this->_input_stream);\n\t\t\tis_array($this->_input_stream) OR $this->_input_stream = array();\n\t\t}\n\n\t\treturn $this->_fetch_from_array($this->_input_stream, $index, $xss_clean);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Set cookie\n\t *\n\t * Accepts an arbitrary number of parameters (up to 7) or an associative\n\t * array in the first parameter containing all the values.\n\t *\n\t * @param\tstring|mixed[]\t$name\t\tCookie name or an array containing parameters\n\t * @param\tstring\t\t$value\t\tCookie value\n\t * @param\tint\t\t$expire\t\tCookie expiration time in seconds\n\t * @param\tstring\t\t$domain\t\tCookie domain (e.g.: '.yourdomain.com')\n\t * @param\tstring\t\t$path\t\tCookie path (default: '/')\n\t * @param\tstring\t\t$prefix\t\tCookie name prefix\n\t * @param\tbool\t\t$secure\t\tWhether to only transfer cookies via SSL\n\t * @param\tbool\t\t$httponly\tWhether to only makes the cookie accessible via HTTP (no javascript)\n\t * @param\tstring\t\t$samesite\tSameSite attribute\n\t * @return\tvoid\n\t */\n\tpublic function set_cookie($name, $value = '', $expire = 0, $domain = '', $path = '/', $prefix = '', $secure = NULL, $httponly = NULL, $samesite = NULL)\n\t{\n\t\tif (is_array($name))\n\t\t{\n\t\t\t// always leave 'name' in last place, as the loop will break otherwise, due to $$item\n\t\t\tforeach (array('value', 'expire', 'domain', 'path', 'prefix', 'secure', 'httponly', 'samesite', 'name') as $item)\n\t\t\t{\n\t\t\t\tif (isset($name[$item]))\n\t\t\t\t{\n\t\t\t\t\t$$item = $name[$item];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ($prefix === '' && config_item('cookie_prefix') !== '')\n\t\t{\n\t\t\t$prefix = config_item('cookie_prefix');\n\t\t}\n\n\t\tif ($domain == '' && config_item('cookie_domain') != '')\n\t\t{\n\t\t\t$domain = config_item('cookie_domain');\n\t\t}\n\n\t\tif ($path === '/' && config_item('cookie_path') !== '/')\n\t\t{\n\t\t\t$path = config_item('cookie_path');\n\t\t}\n\n\t\t$secure = ($secure === NULL && config_item('cookie_secure') !== NULL)\n\t\t\t? (bool) config_item('cookie_secure')\n\t\t\t: (bool) $secure;\n\n\t\t$httponly = ($httponly === NULL && config_item('cookie_httponly') !== NULL)\n\t\t\t? (bool) config_item('cookie_httponly')\n\t\t\t: (bool) $httponly;\n\n\t\tif ( ! is_numeric($expire) OR $expire < 0)\n\t\t{\n\t\t\t$expire = 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$expire = ($expire > 0) ? time() + $expire : 0;\n\t\t}\n\n\t\tisset($samesite) OR $samesite = config_item('cookie_samesite');\n\t\tif (isset($samesite))\n\t\t{\n\t\t\t$samesite = ucfirst(strtolower($samesite));\n\t\t\tin_array($samesite, array('Lax', 'Strict', 'None'), TRUE) OR $samesite = 'Lax';\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$samesite = 'Lax';\n\t\t}\n\n\t\tif ($samesite === 'None' && ! $secure)\n\t\t{\n\t\t\tlog_message('error', $name.' cookie sent with SameSite=None, but without Secure attribute.');\n\t\t}\n\n\t\tif ( ! is_php('7.3'))\n\t\t{\n\t\t\t$maxage = $expire - time();\n\t\t\tif ($maxage < 1)\n\t\t\t{\n\t\t\t\t$maxage = 0;\n\t\t\t}\n\n\t\t\t$cookie_header = 'Set-Cookie: '.$prefix.$name.'='.rawurlencode($value);\n\t\t\t$cookie_header .= ($expire === 0 ? '' : '; Expires='.gmdate('D, d-M-Y H:i:s T', $expire)).'; Max-Age='.$maxage;\n\t\t\t$cookie_header .= '; Path='.$path.($domain !== '' ? '; Domain='.$domain : '');\n\t\t\t$cookie_header .= ($secure ? '; Secure' : '').($httponly ? '; HttpOnly' : '').'; SameSite='.$samesite;\n\t\t\theader($cookie_header);\n\t\t\treturn;\n\t\t}\n\n\t\t$setcookie_options = array(\n\t\t\t'expires' => $expire,\n\t\t\t'path' => $path,\n\t\t\t'domain' => $domain,\n\t\t\t'secure' => $secure,\n\t\t\t'httponly' => $httponly,\n\t\t\t'samesite' => $samesite,\n\t\t);\n\t\tsetcookie($prefix.$name, $value, $setcookie_options);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch the IP Address\n\t *\n\t * Determines and validates the visitor's IP address.\n\t *\n\t * @return\tstring\tIP address\n\t */\n\tpublic function ip_address()\n\t{\n\t\tif ($this->ip_address !== FALSE)\n\t\t{\n\t\t\treturn $this->ip_address;\n\t\t}\n\n\t\t$proxy_ips = config_item('proxy_ips');\n\t\tif ( ! empty($proxy_ips) && ! is_array($proxy_ips))\n\t\t{\n\t\t\t$proxy_ips = explode(',', str_replace(' ', '', $proxy_ips));\n\t\t}\n\n\t\t$this->ip_address = $this->server('REMOTE_ADDR');\n\n\t\tif ($proxy_ips)\n\t\t{\n\t\t\tforeach (array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP') as $header)\n\t\t\t{\n\t\t\t\tif (($spoof = $this->server($header)) !== NULL)\n\t\t\t\t{\n\t\t\t\t\t// Some proxies typically list the whole chain of IP\n\t\t\t\t\t// addresses through which the client has reached us.\n\t\t\t\t\t// e.g. client_ip, proxy_ip1, proxy_ip2, etc.\n\t\t\t\t\tsscanf($spoof, '%[^,]', $spoof);\n\n\t\t\t\t\tif ( ! $this->valid_ip($spoof))\n\t\t\t\t\t{\n\t\t\t\t\t\t$spoof = NULL;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ($spoof)\n\t\t\t{\n\t\t\t\tfor ($i = 0, $c = count($proxy_ips); $i < $c; $i++)\n\t\t\t\t{\n\t\t\t\t\t// Check if we have an IP address or a subnet\n\t\t\t\t\tif (strpos($proxy_ips[$i], '/') === FALSE)\n\t\t\t\t\t{\n\t\t\t\t\t\t// An IP address (and not a subnet) is specified.\n\t\t\t\t\t\t// We can compare right away.\n\t\t\t\t\t\tif ($proxy_ips[$i] === $this->ip_address)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t$this->ip_address = $spoof;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// We have a subnet ... now the heavy lifting begins\n\t\t\t\t\tisset($separator) OR $separator = $this->valid_ip($this->ip_address, 'ipv6') ? ':' : '.';\n\n\t\t\t\t\t// If the proxy entry doesn't match the IP protocol - skip it\n\t\t\t\t\tif (strpos($proxy_ips[$i], $separator) === FALSE)\n\t\t\t\t\t{\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Convert the REMOTE_ADDR IP address to binary, if needed\n\t\t\t\t\tif ( ! isset($ip, $sprintf))\n\t\t\t\t\t{\n\t\t\t\t\t\tif ($separator === ':')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// Make sure we're have the \"full\" IPv6 format\n\t\t\t\t\t\t\t$ip = explode(':',\n\t\t\t\t\t\t\t\tstr_replace('::',\n\t\t\t\t\t\t\t\t\tstr_repeat(':', 9 - substr_count($this->ip_address, ':')),\n\t\t\t\t\t\t\t\t\t$this->ip_address\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\tfor ($j = 0; $j < 8; $j++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t$ip[$j] = intval($ip[$j], 16);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t$sprintf = '%016b%016b%016b%016b%016b%016b%016b%016b';\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t$ip = explode('.', $this->ip_address);\n\t\t\t\t\t\t\t$sprintf = '%08b%08b%08b%08b';\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$ip = vsprintf($sprintf, $ip);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Split the netmask length off the network address\n\t\t\t\t\tsscanf($proxy_ips[$i], '%[^/]/%d', $netaddr, $masklen);\n\n\t\t\t\t\t// Again, an IPv6 address is most likely in a compressed form\n\t\t\t\t\tif ($separator === ':')\n\t\t\t\t\t{\n\t\t\t\t\t\t$netaddr = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($netaddr, ':')), $netaddr));\n\t\t\t\t\t\tfor ($j = 0; $j < 8; $j++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t$netaddr[$j] = intval($netaddr[$j], 16);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t$netaddr = explode('.', $netaddr);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Convert to binary and finally compare\n\t\t\t\t\tif (strncmp($ip, vsprintf($sprintf, $netaddr), $masklen) === 0)\n\t\t\t\t\t{\n\t\t\t\t\t\t$this->ip_address = $spoof;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ( ! $this->valid_ip($this->ip_address))\n\t\t{\n\t\t\treturn $this->ip_address = '0.0.0.0';\n\t\t}\n\n\t\treturn $this->ip_address;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Validate IP Address\n\t *\n\t * @param\tstring\t$ip\tIP address\n\t * @param\tstring\t$which\tIP protocol: 'ipv4' or 'ipv6'\n\t * @return\tbool\n\t */\n\tpublic function valid_ip($ip, $which = '')\n\t{\n\t\tswitch (strtolower($which))\n\t\t{\n\t\t\tcase 'ipv4':\n\t\t\t\t$which = FILTER_FLAG_IPV4;\n\t\t\t\tbreak;\n\t\t\tcase 'ipv6':\n\t\t\t\t$which = FILTER_FLAG_IPV6;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t$which = 0;\n\t\t\t\tbreak;\n\t\t}\n\n\t\treturn (bool) filter_var($ip, FILTER_VALIDATE_IP, $which);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch User Agent string\n\t *\n\t * @return\tstring|null\tUser Agent string or NULL if it doesn't exist\n\t */\n\tpublic function user_agent($xss_clean = FALSE)\n\t{\n\t\treturn $this->_fetch_from_array($_SERVER, 'HTTP_USER_AGENT', $xss_clean);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Request Headers\n\t *\n\t * @param\tbool\t$xss_clean\tWhether to apply XSS filtering\n\t * @return\tarray\n\t */\n\tpublic function request_headers($xss_clean = FALSE)\n\t{\n\t\t// If header is already defined, return it immediately\n\t\tif ( ! empty($this->headers))\n\t\t{\n\t\t\treturn $this->_fetch_from_array($this->headers, NULL, $xss_clean);\n\t\t}\n\n\t\t// In Apache, you can simply call apache_request_headers()\n\t\tif (function_exists('apache_request_headers'))\n\t\t{\n\t\t\t$this->headers = apache_request_headers();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tisset($_SERVER['CONTENT_TYPE']) && $this->headers['Content-Type'] = $_SERVER['CONTENT_TYPE'];\n\n\t\t\tforeach ($_SERVER as $key => $val)\n\t\t\t{\n\t\t\t\tif (sscanf($key, 'HTTP_%s', $header) === 1)\n\t\t\t\t{\n\t\t\t\t\t// take SOME_HEADER and turn it into Some-Header\n\t\t\t\t\t$header = str_replace('_', ' ', strtolower($header));\n\t\t\t\t\t$header = str_replace(' ', '-', ucwords($header));\n\n\t\t\t\t\t$this->headers[$header] = $_SERVER[$key];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $this->_fetch_from_array($this->headers, NULL, $xss_clean);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get Request Header\n\t *\n\t * Returns the value of a single member of the headers class member\n\t *\n\t * @param\tstring\t\t$index\t\tHeader name\n\t * @param\tbool\t\t$xss_clean\tWhether to apply XSS filtering\n\t * @return\tstring|null\tThe requested header on success or NULL on failure\n\t */\n\tpublic function get_request_header($index, $xss_clean = FALSE)\n\t{\n\t\tstatic $headers;\n\n\t\tif ( ! isset($headers))\n\t\t{\n\t\t\tempty($this->headers) && $this->request_headers();\n\t\t\tforeach ($this->headers as $key => $value)\n\t\t\t{\n\t\t\t\t$headers[strtolower($key)] = $value;\n\t\t\t}\n\t\t}\n\n\t\t$index = strtolower($index);\n\n\t\tif ( ! isset($headers[$index]))\n\t\t{\n\t\t\treturn NULL;\n\t\t}\n\n\t\treturn ($xss_clean === TRUE)\n\t\t\t? $this->security->xss_clean($headers[$index])\n\t\t\t: $headers[$index];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Is AJAX request?\n\t *\n\t * Test to see if a request contains the HTTP_X_REQUESTED_WITH header.\n\t *\n\t * @return \tbool\n\t */\n\tpublic function is_ajax_request()\n\t{\n\t\treturn ( ! empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get Request Method\n\t *\n\t * Return the request method\n\t *\n\t * @param\tbool\t$upper\tWhether to return in upper or lower case\n\t *\t\t\t\t(default: FALSE)\n\t * @return \tstring\n\t */\n\tpublic function method($upper = FALSE)\n\t{\n\t\treturn ($upper)\n\t\t\t? strtoupper($this->server('REQUEST_METHOD'))\n\t\t\t: strtolower($this->server('REQUEST_METHOD'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Magic __get()\n\t *\n\t * Allows read access to protected properties\n\t *\n\t * @param\tstring\t$name\n\t * @return\tmixed\n\t */\n\tpublic function __get($name)\n\t{\n\t\tif ($name === 'raw_input_stream')\n\t\t{\n\t\t\tisset($this->_raw_input_stream) OR $this->_raw_input_stream = file_get_contents('php://input');\n\t\t\treturn $this->_raw_input_stream;\n\t\t}\n\t\telseif ($name === 'ip_address')\n\t\t{\n\t\t\treturn $this->ip_address;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "system/core/Lang.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Language Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tLanguage\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/language.html\n */\nclass CI_Lang {\n\n\t/**\n\t * List of translations\n\t *\n\t * @var\tarray\n\t */\n\tpublic $language =\tarray();\n\n\t/**\n\t * List of loaded language files\n\t *\n\t * @var\tarray\n\t */\n\tpublic $is_loaded =\tarray();\n\n\t/**\n\t * Class constructor\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __construct()\n\t{\n\t\tlog_message('info', 'Language Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Load a language file\n\t *\n\t * @param\tmixed\t$langfile\tLanguage file name\n\t * @param\tstring\t$idiom\t\tLanguage name (english, etc.)\n\t * @param\tbool\t$return\t\tWhether to return the loaded array of translations\n\t * @param \tbool\t$add_suffix\tWhether to add suffix to $langfile\n\t * @param \tstring\t$alt_path\tAlternative path to look for the language file\n\t *\n\t * @return\tvoid|string[]\tArray containing translations, if $return is set to TRUE\n\t */\n\tpublic function load($langfile, $idiom = '', $return = FALSE, $add_suffix = TRUE, $alt_path = '')\n\t{\n\t\tif (is_array($langfile))\n\t\t{\n\t\t\tforeach ($langfile as $value)\n\t\t\t{\n\t\t\t\t$this->load($value, $idiom, $return, $add_suffix, $alt_path);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\t$langfile = str_replace('.php', '', $langfile);\n\n\t\tif ($add_suffix === TRUE)\n\t\t{\n\t\t\t$langfile = preg_replace('/_lang$/', '', $langfile).'_lang';\n\t\t}\n\n\t\t$langfile .= '.php';\n\n\t\tif (empty($idiom) OR ! preg_match('/^[a-z_-]+$/i', $idiom))\n\t\t{\n\t\t\t$config =& get_config();\n\t\t\t$idiom = empty($config['language']) ? 'english' : $config['language'];\n\t\t}\n\n\t\tif ($return === FALSE && isset($this->is_loaded[$langfile]) && $this->is_loaded[$langfile] === $idiom)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t// Load the base file, so any others found can override it\n\t\t$basepath = BASEPATH.'language/'.$idiom.'/'.$langfile;\n\t\tif (($found = file_exists($basepath)) === TRUE)\n\t\t{\n\t\t\tinclude($basepath);\n\t\t}\n\n\t\t// Do we have an alternative path to look in?\n\t\tif ($alt_path !== '')\n\t\t{\n\t\t\t$alt_path .= 'language/'.$idiom.'/'.$langfile;\n\t\t\tif (file_exists($alt_path))\n\t\t\t{\n\t\t\t\tinclude($alt_path);\n\t\t\t\t$found = TRUE;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach (get_instance()->load->get_package_paths(TRUE) as $package_path)\n\t\t\t{\n\t\t\t\t$package_path .= 'language/'.$idiom.'/'.$langfile;\n\t\t\t\tif ($basepath !== $package_path && file_exists($package_path))\n\t\t\t\t{\n\t\t\t\t\tinclude($package_path);\n\t\t\t\t\t$found = TRUE;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ($found !== TRUE)\n\t\t{\n\t\t\tshow_error('Unable to load the requested language file: language/'.$idiom.'/'.$langfile);\n\t\t}\n\n\t\tif ( ! isset($lang) OR ! is_array($lang))\n\t\t{\n\t\t\tlog_message('error', 'Language file contains no data: language/'.$idiom.'/'.$langfile);\n\n\t\t\tif ($return === TRUE)\n\t\t\t{\n\t\t\t\treturn array();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif ($return === TRUE)\n\t\t{\n\t\t\treturn $lang;\n\t\t}\n\n\t\t$this->is_loaded[$langfile] = $idiom;\n\t\t$this->language = array_merge($this->language, $lang);\n\n\t\tlog_message('info', 'Language file loaded: language/'.$idiom.'/'.$langfile);\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Language line\n\t *\n\t * Fetches a single line of text from the language array\n\t *\n\t * @param\tstring\t$line\t\tLanguage line key\n\t * @param\tbool\t$log_errors\tWhether to log an error message if the line is not found\n\t * @return\tstring\tTranslation\n\t */\n\tpublic function line($line, $log_errors = TRUE)\n\t{\n\t\t$value = isset($this->language[$line]) ? $this->language[$line] : FALSE;\n\n\t\t// Because killer robots like unicorns!\n\t\tif ($value === FALSE && $log_errors === TRUE)\n\t\t{\n\t\t\tlog_message('error', 'Could not find the language line \"'.$line.'\"');\n\t\t}\n\n\t\treturn $value;\n\t}\n\n}\n"
  },
  {
    "path": "system/core/Loader.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Loader Class\n *\n * Loads framework components.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tLoader\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/loader.html\n */\nclass CI_Loader {\n\n\t// All these are set automatically. Don't mess with them.\n\t/**\n\t * Nesting level of the output buffering mechanism\n\t *\n\t * @var\tint\n\t */\n\tprotected $_ci_ob_level;\n\n\t/**\n\t * List of paths to load views from\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_ci_view_paths =\tarray(VIEWPATH\t=> TRUE);\n\n\t/**\n\t * List of paths to load libraries from\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_ci_library_paths =\tarray(APPPATH, BASEPATH);\n\n\t/**\n\t * List of paths to load models from\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_ci_model_paths =\tarray(APPPATH);\n\n\t/**\n\t * List of paths to load helpers from\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_ci_helper_paths =\tarray(APPPATH, BASEPATH);\n\n\t/**\n\t * List of cached variables\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_ci_cached_vars =\tarray();\n\n\t/**\n\t * Stack of variable arrays to provide nested _ci_load calls with all variables from parent calls\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_ci_load_vars_stack =\tarray();\n\n\t/**\n\t * List of loaded classes\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_ci_classes =\tarray();\n\n\t/**\n\t * List of loaded models\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_ci_models =\tarray();\n\n\t/**\n\t * List of loaded helpers\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_ci_helpers =\tarray();\n\n\t/**\n\t * List of class name mappings\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_ci_varmap =\tarray(\n\t\t'unit_test' => 'unit',\n\t\t'user_agent' => 'agent'\n\t);\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * Sets component load paths, gets the initial output buffering level.\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __construct()\n\t{\n\t\t$this->_ci_ob_level = ob_get_level();\n\t\t$this->_ci_classes =& is_loaded();\n\n\t\tlog_message('info', 'Loader Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Initializer\n\t *\n\t * @todo\tFigure out a way to move this to the constructor\n\t *\t\twithout breaking *package_path*() methods.\n\t * @uses\tCI_Loader::_ci_autoloader()\n\t * @used-by\tCI_Controller::__construct()\n\t * @return\tvoid\n\t */\n\tpublic function initialize()\n\t{\n\t\t$this->_ci_autoloader();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Is Loaded\n\t *\n\t * A utility method to test if a class is in the self::$_ci_classes array.\n\t *\n\t * @used-by\tMainly used by Form Helper function _get_validation_object().\n\t *\n\t * @param \tstring\t\t$class\tClass name to check for\n\t * @return \tstring|bool\tClass object name if loaded or FALSE\n\t */\n\tpublic function is_loaded($class)\n\t{\n\t\treturn array_search(ucfirst($class), $this->_ci_classes, TRUE);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Library Loader\n\t *\n\t * Loads and instantiates libraries.\n\t * Designed to be called from application controllers.\n\t *\n\t * @param\tmixed\t$library\tLibrary name\n\t * @param\tarray\t$params\t\tOptional parameters to pass to the library class constructor\n\t * @param\tstring\t$object_name\tAn optional object name to assign to\n\t * @return\tobject\n\t */\n\tpublic function library($library, $params = NULL, $object_name = NULL)\n\t{\n\t\tif (empty($library))\n\t\t{\n\t\t\treturn $this;\n\t\t}\n\t\telseif (is_array($library))\n\t\t{\n\t\t\tforeach ($library as $key => $value)\n\t\t\t{\n\t\t\t\tif (is_int($key))\n\t\t\t\t{\n\t\t\t\t\t$this->library($value, $params);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$this->library($key, $params, $value);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn $this;\n\t\t}\n\n\t\tif ($params !== NULL && ! is_array($params))\n\t\t{\n\t\t\t$params = NULL;\n\t\t}\n\n\t\t$this->_ci_load_library($library, $params, $object_name);\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Model Loader\n\t *\n\t * Loads and instantiates models.\n\t *\n\t * @param\tmixed\t$model\t\tModel name\n\t * @param\tstring\t$name\t\tAn optional object name to assign to\n\t * @param\tbool\t$db_conn\tAn optional database connection configuration to initialize\n\t * @return\tobject\n\t */\n\tpublic function model($model, $name = '', $db_conn = FALSE)\n\t{\n\t\tif (empty($model))\n\t\t{\n\t\t\treturn $this;\n\t\t}\n\t\telseif (is_array($model))\n\t\t{\n\t\t\tforeach ($model as $key => $value)\n\t\t\t{\n\t\t\t\tis_int($key) ? $this->model($value, '', $db_conn) : $this->model($key, $value, $db_conn);\n\t\t\t}\n\n\t\t\treturn $this;\n\t\t}\n\n\t\t$path = '';\n\n\t\t// Is the model in a sub-folder? If so, parse out the filename and path.\n\t\tif (($last_slash = strrpos($model, '/')) !== FALSE)\n\t\t{\n\t\t\t// The path is in front of the last slash\n\t\t\t$path = substr($model, 0, ++$last_slash);\n\n\t\t\t// And the model name behind it\n\t\t\t$model = substr($model, $last_slash);\n\t\t}\n\n\t\tif (empty($name))\n\t\t{\n\t\t\t$name = $model;\n\t\t}\n\n\t\tif (in_array($name, $this->_ci_models, TRUE))\n\t\t{\n\t\t\treturn $this;\n\t\t}\n\n\t\t$CI =& get_instance();\n\t\tif (isset($CI->$name))\n\t\t{\n\t\t\tthrow new RuntimeException('The model name you are loading is the name of a resource that is already being used: '.$name);\n\t\t}\n\n\t\tif ($db_conn !== FALSE && ! class_exists('CI_DB', FALSE))\n\t\t{\n\t\t\tif ($db_conn === TRUE)\n\t\t\t{\n\t\t\t\t$db_conn = '';\n\t\t\t}\n\n\t\t\t$this->database($db_conn, FALSE, TRUE);\n\t\t}\n\n\t\t// Note: All of the code under this condition used to be just:\n\t\t//\n\t\t//       load_class('Model', 'core');\n\t\t//\n\t\t//       However, load_class() instantiates classes\n\t\t//       to cache them for later use and that prevents\n\t\t//       MY_Model from being an abstract class and is\n\t\t//       sub-optimal otherwise anyway.\n\t\tif ( ! class_exists('CI_Model', FALSE))\n\t\t{\n\t\t\t$app_path = APPPATH.'core'.DIRECTORY_SEPARATOR;\n\t\t\tif (file_exists($app_path.'Model.php'))\n\t\t\t{\n\t\t\t\trequire_once($app_path.'Model.php');\n\t\t\t\tif ( ! class_exists('CI_Model', FALSE))\n\t\t\t\t{\n\t\t\t\t\tthrow new RuntimeException($app_path.\"Model.php exists, but doesn't declare class CI_Model\");\n\t\t\t\t}\n\n\t\t\t\tlog_message('info', 'CI_Model class loaded');\n\t\t\t}\n\t\t\telseif ( ! class_exists('CI_Model', FALSE))\n\t\t\t{\n\t\t\t\trequire_once(BASEPATH.'core'.DIRECTORY_SEPARATOR.'Model.php');\n\t\t\t}\n\n\t\t\t$class = config_item('subclass_prefix').'Model';\n\t\t\tif (file_exists($app_path.$class.'.php'))\n\t\t\t{\n\t\t\t\trequire_once($app_path.$class.'.php');\n\t\t\t\tif ( ! class_exists($class, FALSE))\n\t\t\t\t{\n\t\t\t\t\tthrow new RuntimeException($app_path.$class.\".php exists, but doesn't declare class \".$class);\n\t\t\t\t}\n\n\t\t\t\tlog_message('info', config_item('subclass_prefix').'Model class loaded');\n\t\t\t}\n\t\t}\n\n\t\t$model = ucfirst($model);\n\t\tif ( ! class_exists($model, FALSE))\n\t\t{\n\t\t\tforeach ($this->_ci_model_paths as $mod_path)\n\t\t\t{\n\t\t\t\tif ( ! file_exists($mod_path.'models/'.$path.$model.'.php'))\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\trequire_once($mod_path.'models/'.$path.$model.'.php');\n\t\t\t\tif ( ! class_exists($model, FALSE))\n\t\t\t\t{\n\t\t\t\t\tthrow new RuntimeException($mod_path.\"models/\".$path.$model.\".php exists, but doesn't declare class \".$model);\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif ( ! class_exists($model, FALSE))\n\t\t\t{\n\t\t\t\tthrow new RuntimeException('Unable to locate the model you have specified: '.$model);\n\t\t\t}\n\t\t}\n\n\t\tif ( ! is_subclass_of($model, 'CI_Model'))\n\t\t{\n\t\t\tthrow new RuntimeException(\"Class \".$model.\" doesn't extend CI_Model\");\n\t\t}\n\n\t\t$this->_ci_models[] = $name;\n\t\t$model = new $model();\n\t\t$CI->$name = $model;\n\t\tlog_message('info', 'Model \"'.get_class($model).'\" initialized');\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Database Loader\n\t *\n\t * @param\tmixed\t$params\t\tDatabase configuration options\n\t * @param\tbool\t$return \tWhether to return the database object\n\t * @return\tobject|bool\tDatabase object if $return is set to TRUE,\n\t *\t\t\t\t\tFALSE on failure, CI_Loader instance in any other case\n\t */\n\tpublic function database($params = '', $return = FALSE)\n\t{\n\t\t// Grab the super object\n\t\t$CI =& get_instance();\n\n\t\t// Do we even need to load the database class?\n\t\tif ($return === FALSE && isset($CI->db) && is_object($CI->db) && ! empty($CI->db->conn_id))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\trequire_once(BASEPATH.'database/DB.php');\n\n\t\tif ($return === TRUE)\n\t\t{\n\t\t\treturn DB($params);\n\t\t}\n\n\t\t// Initialize the db variable. Needed to prevent\n\t\t// reference errors with some configurations\n\t\t$CI->db = '';\n\n\t\t// Load the DB class\n\t\t$CI->db =& DB($params);\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Load the Database Utilities Class\n\t *\n\t * @param\tobject\t$db\tDatabase object\n\t * @param\tbool\t$return\tWhether to return the DB Utilities class object or not\n\t * @return\tobject\n\t */\n\tpublic function dbutil($db = NULL, $return = FALSE)\n\t{\n\t\t$CI =& get_instance();\n\n\t\tif ( ! is_object($db) OR ! ($db instanceof CI_DB))\n\t\t{\n\t\t\tclass_exists('CI_DB', FALSE) OR $this->database();\n\t\t\t$db =& $CI->db;\n\t\t}\n\n\t\trequire_once(BASEPATH.'database/DB_utility.php');\n\t\trequire_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_utility.php');\n\t\t$class = 'CI_DB_'.$db->dbdriver.'_utility';\n\n\t\tif ($return === TRUE)\n\t\t{\n\t\t\treturn new $class($db);\n\t\t}\n\n\t\t$CI->dbutil = new $class($db);\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Load the Database Forge Class\n\t *\n\t * @param\tobject\t$db\tDatabase object\n\t * @param\tbool\t$return\tWhether to return the DB Forge class object or not\n\t * @return\tobject\n\t */\n\tpublic function dbforge($db = NULL, $return = FALSE)\n\t{\n\t\t$CI =& get_instance();\n\t\tif ( ! is_object($db) OR ! ($db instanceof CI_DB))\n\t\t{\n\t\t\tclass_exists('CI_DB', FALSE) OR $this->database();\n\t\t\t$db =& $CI->db;\n\t\t}\n\n\t\trequire_once(BASEPATH.'database/DB_forge.php');\n\t\trequire_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_forge.php');\n\n\t\tif ( ! empty($db->subdriver))\n\t\t{\n\t\t\t$driver_path = BASEPATH.'database/drivers/'.$db->dbdriver.'/subdrivers/'.$db->dbdriver.'_'.$db->subdriver.'_forge.php';\n\t\t\tif (file_exists($driver_path))\n\t\t\t{\n\t\t\t\trequire_once($driver_path);\n\t\t\t\t$class = 'CI_DB_'.$db->dbdriver.'_'.$db->subdriver.'_forge';\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$class = 'CI_DB_'.$db->dbdriver.'_forge';\n\t\t}\n\n\t\tif ($return === TRUE)\n\t\t{\n\t\t\treturn new $class($db);\n\t\t}\n\n\t\t$CI->dbforge = new $class($db);\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * View Loader\n\t *\n\t * Loads \"view\" files.\n\t *\n\t * @param\tstring\t$view\tView name\n\t * @param\tarray\t$vars\tAn associative array of data\n\t *\t\t\t\tto be extracted for use in the view\n\t * @param\tbool\t$return\tWhether to return the view output\n\t *\t\t\t\tor leave it to the Output class\n\t * @return\tobject|string\n\t */\n\tpublic function view($view, $vars = array(), $return = FALSE)\n\t{\n\t\treturn $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_prepare_view_vars($vars), '_ci_return' => $return));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Generic File Loader\n\t *\n\t * @param\tstring\t$path\tFile path\n\t * @param\tbool\t$return\tWhether to return the file output\n\t * @return\tobject|string\n\t */\n\tpublic function file($path, $return = FALSE)\n\t{\n\t\treturn $this->_ci_load(array('_ci_path' => $path, '_ci_return' => $return));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Variables\n\t *\n\t * Once variables are set they become available within\n\t * the controller class and its \"view\" files.\n\t *\n\t * @param\tarray|object|string\t$vars\n\t *\t\t\t\t\tAn associative array or object containing values\n\t *\t\t\t\t\tto be set, or a value's name if string\n\t * @param \tstring\t$val\tValue to set, only used if $vars is a string\n\t * @return\tobject\n\t */\n\tpublic function vars($vars, $val = '')\n\t{\n\t\t$vars = is_string($vars)\n\t\t\t? array($vars => $val)\n\t\t\t: $this->_ci_prepare_view_vars($vars);\n\n\t\tforeach ($vars as $key => $val)\n\t\t{\n\t\t\t$this->_ci_cached_vars[$key] = $val;\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Clear Cached Variables\n\t *\n\t * Clears the cached variables.\n\t *\n\t * @return\tCI_Loader\n\t */\n\tpublic function clear_vars()\n\t{\n\t\t$this->_ci_cached_vars = array();\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get Variable\n\t *\n\t * Check if a variable is set and retrieve it.\n\t *\n\t * @param\tstring\t$key\tVariable name\n\t * @return\tmixed\tThe variable or NULL if not found\n\t */\n\tpublic function get_var($key)\n\t{\n\t\treturn isset($this->_ci_cached_vars[$key]) ? $this->_ci_cached_vars[$key] : NULL;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get Variables\n\t *\n\t * Retrieves all loaded variables.\n\t *\n\t * @return\tarray\n\t */\n\tpublic function get_vars()\n\t{\n\t\treturn $this->_ci_cached_vars;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Helper Loader\n\t *\n\t * @param\tstring|string[]\t$helpers\tHelper name(s)\n\t * @return\tobject\n\t */\n\tpublic function helper($helpers = array())\n\t{\n\t\tis_array($helpers) OR $helpers = array($helpers);\n\t\tforeach ($helpers as &$helper)\n\t\t{\n\t\t\t$filename = basename($helper);\n\t\t\t$filepath = ($filename === $helper) ? '' : substr($helper, 0, strlen($helper) - strlen($filename));\n\t\t\t$filename = strtolower(preg_replace('#(_helper)?(\\.php)?$#i', '', $filename)).'_helper';\n\t\t\t$helper   = $filepath.$filename;\n\n\t\t\tif (isset($this->_ci_helpers[$helper]))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Is this a helper extension request?\n\t\t\t$ext_helper = config_item('subclass_prefix').$filename;\n\t\t\t$ext_loaded = FALSE;\n\t\t\tforeach ($this->_ci_helper_paths as $path)\n\t\t\t{\n\t\t\t\tif (file_exists($path.'helpers/'.$ext_helper.'.php'))\n\t\t\t\t{\n\t\t\t\t\tinclude_once($path.'helpers/'.$ext_helper.'.php');\n\t\t\t\t\t$ext_loaded = TRUE;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If we have loaded extensions - check if the base one is here\n\t\t\tif ($ext_loaded === TRUE)\n\t\t\t{\n\t\t\t\t$base_helper = BASEPATH.'helpers/'.$helper.'.php';\n\t\t\t\tif ( ! file_exists($base_helper))\n\t\t\t\t{\n\t\t\t\t\tshow_error('Unable to load the requested file: helpers/'.$helper.'.php');\n\t\t\t\t}\n\n\t\t\t\tinclude_once($base_helper);\n\t\t\t\t$this->_ci_helpers[$helper] = TRUE;\n\t\t\t\tlog_message('info', 'Helper loaded: '.$helper);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// No extensions found ... try loading regular helpers and/or overrides\n\t\t\tforeach ($this->_ci_helper_paths as $path)\n\t\t\t{\n\t\t\t\tif (file_exists($path.'helpers/'.$helper.'.php'))\n\t\t\t\t{\n\t\t\t\t\tinclude_once($path.'helpers/'.$helper.'.php');\n\n\t\t\t\t\t$this->_ci_helpers[$helper] = TRUE;\n\t\t\t\t\tlog_message('info', 'Helper loaded: '.$helper);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// unable to load the helper\n\t\t\tif ( ! isset($this->_ci_helpers[$helper]))\n\t\t\t{\n\t\t\t\tshow_error('Unable to load the requested file: helpers/'.$helper.'.php');\n\t\t\t}\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Load Helpers\n\t *\n\t * An alias for the helper() method in case the developer has\n\t * written the plural form of it.\n\t *\n\t * @uses\tCI_Loader::helper()\n\t * @param\tstring|string[]\t$helpers\tHelper name(s)\n\t * @return\tobject\n\t */\n\tpublic function helpers($helpers = array())\n\t{\n\t\treturn $this->helper($helpers);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Language Loader\n\t *\n\t * Loads language files.\n\t *\n\t * @param\tstring|string[]\t$files\tList of language file names to load\n\t * @param\tstring\t\tLanguage name\n\t * @return\tobject\n\t */\n\tpublic function language($files, $lang = '')\n\t{\n\t\tget_instance()->lang->load($files, $lang);\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Config Loader\n\t *\n\t * Loads a config file (an alias for CI_Config::load()).\n\t *\n\t * @uses\tCI_Config::load()\n\t * @param\tstring\t$file\t\t\tConfiguration file name\n\t * @param\tbool\t$use_sections\t\tWhether configuration values should be loaded into their own section\n\t * @param\tbool\t$fail_gracefully\tWhether to just return FALSE or display an error message\n\t * @return\tbool\tTRUE if the file was loaded correctly or FALSE on failure\n\t */\n\tpublic function config($file, $use_sections = FALSE, $fail_gracefully = FALSE)\n\t{\n\t\treturn get_instance()->config->load($file, $use_sections, $fail_gracefully);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Driver Loader\n\t *\n\t * Loads a driver library.\n\t *\n\t * @param\tstring|string[]\t$library\tDriver name(s)\n\t * @param\tarray\t\t$params\t\tOptional parameters to pass to the driver\n\t * @param\tstring\t\t$object_name\tAn optional object name to assign to\n\t *\n\t * @return\tobject|bool\tObject or FALSE on failure if $library is a string\n\t *\t\t\t\tand $object_name is set. CI_Loader instance otherwise.\n\t */\n\tpublic function driver($library, $params = NULL, $object_name = NULL)\n\t{\n\t\tif (is_array($library))\n\t\t{\n\t\t\tforeach ($library as $key => $value)\n\t\t\t{\n\t\t\t\tif (is_int($key))\n\t\t\t\t{\n\t\t\t\t\t$this->driver($value, $params);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$this->driver($key, $params, $value);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn $this;\n\t\t}\n\t\telseif (empty($library))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif ( ! class_exists('CI_Driver_Library', FALSE))\n\t\t{\n\t\t\t// We aren't instantiating an object here, just making the base class available\n\t\t\trequire BASEPATH.'libraries/Driver.php';\n\t\t}\n\n\t\t// We can save the loader some time since Drivers will *always* be in a subfolder,\n\t\t// and typically identically named to the library\n\t\tif ( ! strpos($library, '/'))\n\t\t{\n\t\t\t$library = ucfirst($library).'/'.$library;\n\t\t}\n\n\t\treturn $this->library($library, $params, $object_name);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Add Package Path\n\t *\n\t * Prepends a parent path to the library, model, helper and config\n\t * path arrays.\n\t *\n\t * @see\tCI_Loader::$_ci_library_paths\n\t * @see\tCI_Loader::$_ci_model_paths\n\t * @see CI_Loader::$_ci_helper_paths\n\t * @see CI_Config::$_config_paths\n\t *\n\t * @param\tstring\t$path\t\tPath to add\n\t * @param \tbool\t$view_cascade\t(default: TRUE)\n\t * @return\tobject\n\t */\n\tpublic function add_package_path($path, $view_cascade = TRUE)\n\t{\n\t\t$path = rtrim($path, '/').'/';\n\n\t\tarray_unshift($this->_ci_library_paths, $path);\n\t\tarray_unshift($this->_ci_model_paths, $path);\n\t\tarray_unshift($this->_ci_helper_paths, $path);\n\n\t\t$this->_ci_view_paths = array($path.'views/' => $view_cascade) + $this->_ci_view_paths;\n\n\t\t// Add config file path\n\t\t$config =& $this->_ci_get_component('config');\n\t\t$config->_config_paths[] = $path;\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get Package Paths\n\t *\n\t * Return a list of all package paths.\n\t *\n\t * @param\tbool\t$include_base\tWhether to include BASEPATH (default: FALSE)\n\t * @return\tarray\n\t */\n\tpublic function get_package_paths($include_base = FALSE)\n\t{\n\t\treturn ($include_base === TRUE) ? $this->_ci_library_paths : $this->_ci_model_paths;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Remove Package Path\n\t *\n\t * Remove a path from the library, model, helper and/or config\n\t * path arrays if it exists. If no path is provided, the most recently\n\t * added path will be removed removed.\n\t *\n\t * @param\tstring\t$path\tPath to remove\n\t * @return\tobject\n\t */\n\tpublic function remove_package_path($path = '')\n\t{\n\t\t$config =& $this->_ci_get_component('config');\n\n\t\tif ($path === '')\n\t\t{\n\t\t\tarray_shift($this->_ci_library_paths);\n\t\t\tarray_shift($this->_ci_model_paths);\n\t\t\tarray_shift($this->_ci_helper_paths);\n\t\t\tarray_shift($this->_ci_view_paths);\n\t\t\tarray_pop($config->_config_paths);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$path = rtrim($path, '/').'/';\n\t\t\tforeach (array('_ci_library_paths', '_ci_model_paths', '_ci_helper_paths') as $var)\n\t\t\t{\n\t\t\t\tif (($key = array_search($path, $this->{$var})) !== FALSE)\n\t\t\t\t{\n\t\t\t\t\tunset($this->{$var}[$key]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (isset($this->_ci_view_paths[$path.'views/']))\n\t\t\t{\n\t\t\t\tunset($this->_ci_view_paths[$path.'views/']);\n\t\t\t}\n\n\t\t\tif (($key = array_search($path, $config->_config_paths)) !== FALSE)\n\t\t\t{\n\t\t\t\tunset($config->_config_paths[$key]);\n\t\t\t}\n\t\t}\n\n\t\t// make sure the application default paths are still in the array\n\t\t$this->_ci_library_paths = array_unique(array_merge($this->_ci_library_paths, array(APPPATH, BASEPATH)));\n\t\t$this->_ci_helper_paths = array_unique(array_merge($this->_ci_helper_paths, array(APPPATH, BASEPATH)));\n\t\t$this->_ci_model_paths = array_unique(array_merge($this->_ci_model_paths, array(APPPATH)));\n\t\t$this->_ci_view_paths = array_merge($this->_ci_view_paths, array(APPPATH.'views/' => TRUE));\n\t\t$config->_config_paths = array_unique(array_merge($config->_config_paths, array(APPPATH)));\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Internal CI Data Loader\n\t *\n\t * Used to load views and files.\n\t *\n\t * Variables are prefixed with _ci_ to avoid symbol collision with\n\t * variables made available to view files.\n\t *\n\t * @used-by\tCI_Loader::view()\n\t * @used-by\tCI_Loader::file()\n\t * @param\tarray\t$_ci_data\tData to load\n\t * @return\tobject\n\t */\n\tprotected function _ci_load($_ci_data)\n\t{\n\t\t// Set the default data variables\n\t\tforeach (array('_ci_view', '_ci_vars', '_ci_path', '_ci_return') as $_ci_val)\n\t\t{\n\t\t\t$$_ci_val = isset($_ci_data[$_ci_val]) ? $_ci_data[$_ci_val] : FALSE;\n\t\t}\n\n\t\t$file_exists = FALSE;\n\n\t\t// Set the path to the requested file\n\t\tif (is_string($_ci_path) && $_ci_path !== '')\n\t\t{\n\t\t\t$_ci_x = explode('/', $_ci_path);\n\t\t\t$_ci_file = end($_ci_x);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);\n\t\t\t$_ci_file = ($_ci_ext === '') ? $_ci_view.'.php' : $_ci_view;\n\n\t\t\tforeach ($this->_ci_view_paths as $_ci_view_file => $cascade)\n\t\t\t{\n\t\t\t\tif (file_exists($_ci_view_file.$_ci_file))\n\t\t\t\t{\n\t\t\t\t\t$_ci_path = $_ci_view_file.$_ci_file;\n\t\t\t\t\t$file_exists = TRUE;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif ( ! $cascade)\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ( ! $file_exists && ! file_exists($_ci_path))\n\t\t{\n\t\t\tshow_error('Unable to load the requested file: '.$_ci_file);\n\t\t}\n\n\t\t// This allows anything loaded using $this->load (views, files, etc.)\n\t\t// to become accessible from within the Controller and Model functions.\n\t\t$_ci_CI =& get_instance();\n\t\tforeach (get_object_vars($_ci_CI) as $_ci_key => $_ci_var)\n\t\t{\n\t\t\tif ( ! isset($this->$_ci_key))\n\t\t\t{\n\t\t\t\t$this->$_ci_key =& $_ci_CI->$_ci_key;\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * Extract and stack variables\n\t\t *\n\t\t * You can either set variables using the dedicated $this->load->vars()\n\t\t * function or via the second parameter of this function. We'll merge\n\t\t * the two types so that loaded views and files have access to these\n\t\t * variables.\n\t\t * Additionally we want all subsequent nested _ci_load() calls embedded\n\t\t * within the current file to 'inherit' all variables that are\n\t\t * accessible to the current file. For this purpose we push the current\n\t\t * variable configuration (_ci_vars) to the stack and remove it again\n\t\t * after the file or view is completely loaded. Nested _ci_load() calls\n\t\t * within the current file extend the stack with their variable\n\t\t * configuration.\n\t\t */\n\n\t\tis_array($_ci_vars) OR $_ci_vars = array();\n\n\t\t// Include the global cached vars into the current _ci_vars if needed\n\t\tempty($this->_ci_cached_vars) OR $_ci_vars = array_merge($this->_ci_cached_vars, $_ci_vars);\n\n\t\t// Merge the last variable configuration from a parent _ci_load()\n\t\t// call into the current _ci_vars\n\t\tif ( ! empty($this->_ci_load_vars_stack))\n\t\t{\n\t\t\t$previous_variable_configuration = end($this->_ci_load_vars_stack);\n\t\t\t$_ci_vars = array_merge($previous_variable_configuration, $_ci_vars);\n\t\t}\n\n\t\tarray_push($this->_ci_load_vars_stack, $_ci_vars);\n\t\textract($_ci_vars);\n\n\t\t/**\n\t\t * Buffer the output\n\t\t *\n\t\t * We buffer the output for two reasons:\n\t\t * 1. Speed. You get a significant speed boost.\n\t\t * 2. So that the final rendered template can be post-processed by\n\t\t *\tthe output class. Why do we need post processing? For one thing,\n\t\t *\tin order to show the elapsed page load time. Unless we can\n\t\t *\tintercept the content right before it's sent to the browser and\n\t\t *\tthen stop the timer it won't be accurate.\n\t\t */\n\t\tob_start();\n\n\t\tinclude($_ci_path); // include() vs include_once() allows for multiple views with the same name\n\t\tlog_message('info', 'File loaded: '.$_ci_path);\n\n\t\t// Remove current _ci_vars from stack\n\t\tarray_pop($this->_ci_load_vars_stack);\n\n\t\t// Return the file data if requested\n\t\tif ($_ci_return === TRUE)\n\t\t{\n\t\t\t$buffer = ob_get_contents();\n\t\t\t@ob_end_clean();\n\t\t\treturn $buffer;\n\t\t}\n\n\t\t/*\n\t\t * Flush the buffer... or buff the flusher?\n\t\t *\n\t\t * In order to permit views to be nested within\n\t\t * other views, we need to flush the content back out whenever\n\t\t * we are beyond the first level of output buffering so that\n\t\t * it can be seen and included properly by the first included\n\t\t * template and any subsequent ones. Oy!\n\t\t */\n\t\tif (ob_get_level() > $this->_ci_ob_level + 1)\n\t\t{\n\t\t\tob_end_flush();\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$_ci_CI->output->append_output(ob_get_contents());\n\t\t\t@ob_end_clean();\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Internal CI Library Loader\n\t *\n\t * @used-by\tCI_Loader::library()\n\t * @uses\tCI_Loader::_ci_init_library()\n\t *\n\t * @param\tstring\t$class\t\tClass name to load\n\t * @param\tmixed\t$params\t\tOptional parameters to pass to the class constructor\n\t * @param\tstring\t$object_name\tOptional object name to assign to\n\t * @return\tvoid\n\t */\n\tprotected function _ci_load_library($class, $params = NULL, $object_name = NULL)\n\t{\n\t\t// Get the class name, and while we're at it trim any slashes.\n\t\t// The directory path can be included as part of the class name,\n\t\t// but we don't want a leading slash\n\t\t$class = str_replace('.php', '', trim($class, '/'));\n\n\t\t// Was the path included with the class name?\n\t\t// We look for a slash to determine this\n\t\tif (($last_slash = strrpos($class, '/')) !== FALSE)\n\t\t{\n\t\t\t// Extract the path\n\t\t\t$subdir = substr($class, 0, ++$last_slash);\n\n\t\t\t// Get the filename from the path\n\t\t\t$class = substr($class, $last_slash);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$subdir = '';\n\t\t}\n\n\t\t$class = ucfirst($class);\n\n\t\t// Is this a stock library? There are a few special conditions if so ...\n\t\tif (file_exists(BASEPATH.'libraries/'.$subdir.$class.'.php'))\n\t\t{\n\t\t\treturn $this->_ci_load_stock_library($class, $subdir, $params, $object_name);\n\t\t}\n\n\t\t// Safety: Was the class already loaded by a previous call?\n\t\tif (class_exists($class, FALSE))\n\t\t{\n\t\t\t$property = $object_name;\n\t\t\tif (empty($property))\n\t\t\t{\n\t\t\t\t$property = strtolower($class);\n\t\t\t\tisset($this->_ci_varmap[$property]) && $property = $this->_ci_varmap[$property];\n\t\t\t}\n\n\t\t\t$CI =& get_instance();\n\t\t\tif (isset($CI->$property))\n\t\t\t{\n\t\t\t\tlog_message('debug', $class.' class already loaded. Second attempt ignored.');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\treturn $this->_ci_init_library($class, '', $params, $object_name);\n\t\t}\n\n\t\t// Let's search for the requested library file and load it.\n\t\tforeach ($this->_ci_library_paths as $path)\n\t\t{\n\t\t\t// BASEPATH has already been checked for\n\t\t\tif ($path === BASEPATH)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$filepath = $path.'libraries/'.$subdir.$class.'.php';\n\t\t\t// Does the file exist? No? Bummer...\n\t\t\tif ( ! file_exists($filepath))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tinclude_once($filepath);\n\t\t\treturn $this->_ci_init_library($class, '', $params, $object_name);\n\t\t}\n\n\t\t// One last attempt. Maybe the library is in a subdirectory, but it wasn't specified?\n\t\tif ($subdir === '')\n\t\t{\n\t\t\treturn $this->_ci_load_library($class.'/'.$class, $params, $object_name);\n\t\t}\n\n\t\t// If we got this far we were unable to find the requested class.\n\t\tlog_message('error', 'Unable to load the requested class: '.$class);\n\t\tshow_error('Unable to load the requested class: '.$class);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Internal CI Stock Library Loader\n\t *\n\t * @used-by\tCI_Loader::_ci_load_library()\n\t * @uses\tCI_Loader::_ci_init_library()\n\t *\n\t * @param\tstring\t$library_name\tLibrary name to load\n\t * @param\tstring\t$file_path\tPath to the library filename, relative to libraries/\n\t * @param\tmixed\t$params\t\tOptional parameters to pass to the class constructor\n\t * @param\tstring\t$object_name\tOptional object name to assign to\n\t * @return\tvoid\n\t */\n\tprotected function _ci_load_stock_library($library_name, $file_path, $params, $object_name)\n\t{\n\t\t$prefix = 'CI_';\n\n\t\tif (class_exists($prefix.$library_name, FALSE))\n\t\t{\n\t\t\tif (class_exists(config_item('subclass_prefix').$library_name, FALSE))\n\t\t\t{\n\t\t\t\t$prefix = config_item('subclass_prefix');\n\t\t\t}\n\n\t\t\t$property = $object_name;\n\t\t\tif (empty($property))\n\t\t\t{\n\t\t\t\t$property = strtolower($library_name);\n\t\t\t\tisset($this->_ci_varmap[$property]) && $property = $this->_ci_varmap[$property];\n\t\t\t}\n\n\t\t\t$CI =& get_instance();\n\t\t\tif ( ! isset($CI->$property))\n\t\t\t{\n\t\t\t\treturn $this->_ci_init_library($library_name, $prefix, $params, $object_name);\n\t\t\t}\n\n\t\t\tlog_message('debug', $library_name.' class already loaded. Second attempt ignored.');\n\t\t\treturn;\n\t\t}\n\n\t\t$paths = $this->_ci_library_paths;\n\t\tarray_pop($paths); // BASEPATH\n\t\tarray_pop($paths); // APPPATH (needs to be the first path checked)\n\t\tarray_unshift($paths, APPPATH);\n\n\t\tforeach ($paths as $path)\n\t\t{\n\t\t\tif (file_exists($path = $path.'libraries/'.$file_path.$library_name.'.php'))\n\t\t\t{\n\t\t\t\t// Override\n\t\t\t\tinclude_once($path);\n\t\t\t\tif (class_exists($prefix.$library_name, FALSE))\n\t\t\t\t{\n\t\t\t\t\treturn $this->_ci_init_library($library_name, $prefix, $params, $object_name);\n\t\t\t\t}\n\n\t\t\t\tlog_message('debug', $path.' exists, but does not declare '.$prefix.$library_name);\n\t\t\t}\n\t\t}\n\n\t\tinclude_once(BASEPATH.'libraries/'.$file_path.$library_name.'.php');\n\n\t\t// Check for extensions\n\t\t$subclass = config_item('subclass_prefix').$library_name;\n\t\tforeach ($paths as $path)\n\t\t{\n\t\t\tif (file_exists($path = $path.'libraries/'.$file_path.$subclass.'.php'))\n\t\t\t{\n\t\t\t\tinclude_once($path);\n\t\t\t\tif (class_exists($subclass, FALSE))\n\t\t\t\t{\n\t\t\t\t\t$prefix = config_item('subclass_prefix');\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tlog_message('debug', $path.' exists, but does not declare '.$subclass);\n\t\t\t}\n\t\t}\n\n\t\treturn $this->_ci_init_library($library_name, $prefix, $params, $object_name);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Internal CI Library Instantiator\n\t *\n\t * @used-by\tCI_Loader::_ci_load_stock_library()\n\t * @used-by\tCI_Loader::_ci_load_library()\n\t *\n\t * @param\tstring\t\t$class\t\tClass name\n\t * @param\tstring\t\t$prefix\t\tClass name prefix\n\t * @param\tarray|null|bool\t$config\t\tOptional configuration to pass to the class constructor:\n\t *\t\t\t\t\t\tFALSE to skip;\n\t *\t\t\t\t\t\tNULL to search in config paths;\n\t *\t\t\t\t\t\tarray containing configuration data\n\t * @param\tstring\t\t$object_name\tOptional object name to assign to\n\t * @return\tvoid\n\t */\n\tprotected function _ci_init_library($class, $prefix, $config = FALSE, $object_name = NULL)\n\t{\n\t\t// Is there an associated config file for this class? Note: these should always be lowercase\n\t\tif ($config === NULL)\n\t\t{\n\t\t\t// Fetch the config paths containing any package paths\n\t\t\t$config_component = $this->_ci_get_component('config');\n\n\t\t\tif (is_array($config_component->_config_paths))\n\t\t\t{\n\t\t\t\t$found = FALSE;\n\t\t\t\tforeach ($config_component->_config_paths as $path)\n\t\t\t\t{\n\t\t\t\t\t// We test for both uppercase and lowercase, for servers that\n\t\t\t\t\t// are case-sensitive with regard to file names. Load global first,\n\t\t\t\t\t// override with environment next\n\t\t\t\t\tif (file_exists($path.'config/'.strtolower($class).'.php'))\n\t\t\t\t\t{\n\t\t\t\t\t\tinclude($path.'config/'.strtolower($class).'.php');\n\t\t\t\t\t\t$found = TRUE;\n\t\t\t\t\t}\n\t\t\t\t\telseif (file_exists($path.'config/'.ucfirst(strtolower($class)).'.php'))\n\t\t\t\t\t{\n\t\t\t\t\t\tinclude($path.'config/'.ucfirst(strtolower($class)).'.php');\n\t\t\t\t\t\t$found = TRUE;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (file_exists($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'))\n\t\t\t\t\t{\n\t\t\t\t\t\tinclude($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php');\n\t\t\t\t\t\t$found = TRUE;\n\t\t\t\t\t}\n\t\t\t\t\telseif (file_exists($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'))\n\t\t\t\t\t{\n\t\t\t\t\t\tinclude($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php');\n\t\t\t\t\t\t$found = TRUE;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Break on the first found configuration, thus package\n\t\t\t\t\t// files are not overridden by default paths\n\t\t\t\t\tif ($found === TRUE)\n\t\t\t\t\t{\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$class_name = $prefix.$class;\n\n\t\t// Is the class name valid?\n\t\tif ( ! class_exists($class_name, FALSE))\n\t\t{\n\t\t\tlog_message('error', 'Non-existent class: '.$class_name);\n\t\t\tshow_error('Non-existent class: '.$class_name);\n\t\t}\n\n\t\t// Set the variable name we will assign the class to\n\t\t// Was a custom class name supplied? If so we'll use it\n\t\tif (empty($object_name))\n\t\t{\n\t\t\t$object_name = strtolower($class);\n\t\t\tif (isset($this->_ci_varmap[$object_name]))\n\t\t\t{\n\t\t\t\t$object_name = $this->_ci_varmap[$object_name];\n\t\t\t}\n\t\t}\n\n\t\t// Don't overwrite existing properties\n\t\t$CI =& get_instance();\n\t\tif (isset($CI->$object_name))\n\t\t{\n\t\t\tif ($CI->$object_name instanceof $class_name)\n\t\t\t{\n\t\t\t\tlog_message('debug', $class_name.\" has already been instantiated as '\".$object_name.\"'. Second attempt aborted.\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tshow_error(\"Resource '\".$object_name.\"' already exists and is not a \".$class_name.\" instance.\");\n\t\t}\n\n\t\t// Save the class name and object name\n\t\t$this->_ci_classes[$object_name] = $class;\n\n\t\t// Instantiate the class\n\t\t$CI->$object_name = isset($config)\n\t\t\t? new $class_name($config)\n\t\t\t: new $class_name();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * CI Autoloader\n\t *\n\t * Loads component listed in the config/autoload.php file.\n\t *\n\t * @used-by\tCI_Loader::initialize()\n\t * @return\tvoid\n\t */\n\tprotected function _ci_autoloader()\n\t{\n\t\tif (file_exists(APPPATH.'config/autoload.php'))\n\t\t{\n\t\t\tinclude(APPPATH.'config/autoload.php');\n\t\t}\n\n\t\tif (file_exists(APPPATH.'config/'.ENVIRONMENT.'/autoload.php'))\n\t\t{\n\t\t\tinclude(APPPATH.'config/'.ENVIRONMENT.'/autoload.php');\n\t\t}\n\n\t\tif ( ! isset($autoload))\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t// Autoload packages\n\t\tif (isset($autoload['packages']))\n\t\t{\n\t\t\tforeach ($autoload['packages'] as $package_path)\n\t\t\t{\n\t\t\t\t$this->add_package_path($package_path);\n\t\t\t}\n\t\t}\n\n\t\t// Load any custom config file\n\t\tif (count($autoload['config']) > 0)\n\t\t{\n\t\t\tforeach ($autoload['config'] as $val)\n\t\t\t{\n\t\t\t\t$this->config($val);\n\t\t\t}\n\t\t}\n\n\t\t// Autoload helpers and languages\n\t\tforeach (array('helper', 'language') as $type)\n\t\t{\n\t\t\tif (isset($autoload[$type]) && count($autoload[$type]) > 0)\n\t\t\t{\n\t\t\t\t$this->$type($autoload[$type]);\n\t\t\t}\n\t\t}\n\n\t\t// Autoload drivers\n\t\tif (isset($autoload['drivers']))\n\t\t{\n\t\t\t$this->driver($autoload['drivers']);\n\t\t}\n\n\t\t// Load libraries\n\t\tif (isset($autoload['libraries']) && count($autoload['libraries']) > 0)\n\t\t{\n\t\t\t// Load the database driver.\n\t\t\tif (in_array('database', $autoload['libraries']))\n\t\t\t{\n\t\t\t\t$this->database();\n\t\t\t\t$autoload['libraries'] = array_diff($autoload['libraries'], array('database'));\n\t\t\t}\n\n\t\t\t// Load all other libraries\n\t\t\t$this->library($autoload['libraries']);\n\t\t}\n\n\t\t// Autoload models\n\t\tif (isset($autoload['model']))\n\t\t{\n\t\t\t$this->model($autoload['model']);\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Prepare variables for _ci_vars, to be later extract()-ed inside views\n\t *\n\t * Converts objects to associative arrays and filters-out internal\n\t * variable names (i.e. keys prefixed with '_ci_').\n\t *\n\t * @param\tmixed\t$vars\n\t * @return\tarray\n\t */\n\tprotected function _ci_prepare_view_vars($vars)\n\t{\n\t\tif ( ! is_array($vars))\n\t\t{\n\t\t\t$vars = is_object($vars)\n\t\t\t\t? get_object_vars($vars)\n\t\t\t\t: array();\n\t\t}\n\n\t\tforeach (array_keys($vars) as $key)\n\t\t{\n\t\t\tif (strncmp($key, '_ci_', 4) === 0)\n\t\t\t{\n\t\t\t\tunset($vars[$key]);\n\t\t\t}\n\t\t}\n\n\t\treturn $vars;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * CI Component getter\n\t *\n\t * Get a reference to a specific library or model.\n\t *\n\t * @param \tstring\t$component\tComponent name\n\t * @return\tbool\n\t */\n\tprotected function &_ci_get_component($component)\n\t{\n\t\t$CI =& get_instance();\n\t\treturn $CI->$component;\n\t}\n}\n"
  },
  {
    "path": "system/core/Log.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Logging Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tLogging\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/general/errors.html\n */\nclass CI_Log {\n\n\t/**\n\t * Path to save log files\n\t *\n\t * @var string\n\t */\n\tprotected $_log_path;\n\n\t/**\n\t * Log filename\n\t *\n\t * @var string\n\t */\n\tprotected $_log_filename;\n\n\t/**\n\t * File permissions\n\t *\n\t * @var\tint\n\t */\n\tprotected $_file_permissions = 0644;\n\n\t/**\n\t * Level of logging\n\t *\n\t * @var int\n\t */\n\tprotected $_threshold = 1;\n\n\t/**\n\t * Array of threshold levels to log\n\t *\n\t * @var array\n\t */\n\tprotected $_threshold_array = array();\n\n\t/**\n\t * Format of timestamp for log files\n\t *\n\t * @var string\n\t */\n\tprotected $_date_fmt = 'Y-m-d H:i:s';\n\n\t/**\n\t * Whether or not the logger can write to the log files\n\t *\n\t * @var bool\n\t */\n\tprotected $_enabled = TRUE;\n\n\t/**\n\t * Predefined logging levels\n\t *\n\t * @var array\n\t */\n\tprotected $_levels = array('ERROR' => 1, 'DEBUG' => 2, 'INFO' => 3, 'ALL' => 4);\n\n\t/**\n\t * mbstring.func_overload flag\n\t *\n\t * @var\tbool\n\t */\n\tprotected static $func_overload;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __construct()\n\t{\n\t\t$config =& get_config();\n\n\t\tisset(self::$func_overload) OR self::$func_overload = ( ! is_php('8.0') && extension_loaded('mbstring') && @ini_get('mbstring.func_overload'));\n\n\t\t$this->_log_path = ($config['log_path'] !== '')\n\t\t\t? rtrim($config['log_path'], '/\\\\').DIRECTORY_SEPARATOR : APPPATH.'logs'.DIRECTORY_SEPARATOR;\n\n\t\t$this->_log_filename = (isset($config['log_filename']) && $config['log_filename'] !== '')\n\t\t\t? $config['log_filename'] : 'log-'.date('Y-m-d').'.php';\n\n\t\tfile_exists($this->_log_path) OR mkdir($this->_log_path, 0755, TRUE);\n\n\t\tif ( ! is_dir($this->_log_path) OR ! is_really_writable($this->_log_path))\n\t\t{\n\t\t\t$this->_enabled = FALSE;\n\t\t}\n\n\t\tif (is_numeric($config['log_threshold']))\n\t\t{\n\t\t\t$this->_threshold = (int) $config['log_threshold'];\n\t\t}\n\t\telseif (is_array($config['log_threshold']))\n\t\t{\n\t\t\t$this->_threshold = 0;\n\t\t\t$this->_threshold_array = array_flip($config['log_threshold']);\n\t\t}\n\n\t\tif ( ! empty($config['log_date_format']))\n\t\t{\n\t\t\t$this->_date_fmt = $config['log_date_format'];\n\t\t}\n\n\t\tif ( ! empty($config['log_file_permissions']) && is_int($config['log_file_permissions']))\n\t\t{\n\t\t\t$this->_file_permissions = $config['log_file_permissions'];\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Write Log File\n\t *\n\t * Generally this function will be called using the global log_message() function\n\t *\n\t * @param\tstring\t$level \tThe error level: 'error', 'debug' or 'info'\n\t * @param\tstring\t$msg \tThe error message\n\t * @return\tbool\n\t */\n\tpublic function write_log($level, $msg)\n\t{\n\t\tif ($this->_enabled === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$level = strtoupper($level);\n\n\t\tif (( ! isset($this->_levels[$level]) OR ($this->_levels[$level] > $this->_threshold))\n\t\t\t&& ! isset($this->_threshold_array[$this->_levels[$level]]))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$filepath = $this->_log_path.$this->_log_filename;\n\t\t$message = '';\n\n\t\tif ( ! file_exists($filepath))\n\t\t{\n\t\t\t$newfile = TRUE;\n\t\t\t// Only add protection to php files\n\t\t\tif (substr($this->_log_filename, -3, 3) === 'php')\n\t\t\t{\n\t\t\t\t$message .= \"<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?>\\n\\n\";\n\t\t\t}\n\t\t}\n\n\t\tif ( ! $fp = @fopen($filepath, 'ab'))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tflock($fp, LOCK_EX);\n\n\t\t// Instantiating DateTime with microseconds appended to initial date is needed for proper support of this format\n\t\tif (strpos($this->_date_fmt, 'u') !== FALSE)\n\t\t{\n\t\t\t$microtime_full = microtime(TRUE);\n\t\t\t$microtime_short = sprintf(\"%06d\", ($microtime_full - floor($microtime_full)) * 1000000);\n\t\t\t$date = new DateTime(date('Y-m-d H:i:s.'.$microtime_short, $microtime_full));\n\t\t\t$date = $date->format($this->_date_fmt);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$date = date($this->_date_fmt);\n\t\t}\n\n\t\t$message .= $this->_format_line($level, $date, $msg);\n\n\t\tfor ($written = 0, $length = self::strlen($message); $written < $length; $written += $result)\n\t\t{\n\t\t\tif (($result = fwrite($fp, self::substr($message, $written))) === FALSE)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tflock($fp, LOCK_UN);\n\t\tfclose($fp);\n\n\t\tif (isset($newfile) && $newfile === TRUE)\n\t\t{\n\t\t\tchmod($filepath, $this->_file_permissions);\n\t\t}\n\n\t\treturn is_int($result);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Format the log line.\n\t *\n\t * This is for extensibility of log formatting\n\t * If you want to change the log format, extend the CI_Log class and override this method\n\t *\n\t * @param\tstring\t$level \tThe error level\n\t * @param\tstring\t$date \tFormatted date string\n\t * @param\tstring\t$message \tThe log message\n\t * @return\tstring\tFormatted log line with a new line character at the end\n\t */\n\tprotected function _format_line($level, $date, $message)\n\t{\n\t\treturn $level.' - '.$date.' --> '.$message.PHP_EOL;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Byte-safe strlen()\n\t *\n\t * @param\tstring\t$str\n\t * @return\tint\n\t */\n\tprotected static function strlen($str)\n\t{\n\t\treturn (self::$func_overload)\n\t\t\t? mb_strlen($str, '8bit')\n\t\t\t: strlen($str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Byte-safe substr()\n\t *\n\t * @param\tstring\t$str\n\t * @param\tint\t$start\n\t * @param\tint\t$length\n\t * @return\tstring\n\t */\n\tprotected static function substr($str, $start, $length = NULL)\n\t{\n\t\tif (self::$func_overload)\n\t\t{\n\t\t\treturn mb_substr($str, $start, $length, '8bit');\n\t\t}\n\n\t\treturn isset($length)\n\t\t\t? substr($str, $start, $length)\n\t\t\t: substr($str, $start);\n\t}\n}\n"
  },
  {
    "path": "system/core/Model.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Model Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tLibraries\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/config.html\n */\nclass CI_Model {\n\n\t/**\n\t * __get magic\n\t *\n\t * Allows models to access CI's loaded classes using the same\n\t * syntax as controllers.\n\t *\n\t * @param\tstring\t$key\n\t */\n\tpublic function __get($key)\n\t{\n\t\t// Debugging note:\n\t\t//\tIf you're here because you're getting an error message\n\t\t//\tsaying 'Undefined Property: system/core/Model.php', it's\n\t\t//\tmost likely a typo in your model code.\n\t\treturn get_instance()->$key;\n\t}\n\n}\n"
  },
  {
    "path": "system/core/Output.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Output Class\n *\n * Responsible for sending final output to the browser.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tOutput\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/output.html\n */\nclass CI_Output {\n\n\t/**\n\t * Final output string\n\t *\n\t * @var\tstring\n\t */\n\tpublic $final_output = '';\n\n\t/**\n\t * Cache expiration time\n\t *\n\t * @var\tint\n\t */\n\tpublic $cache_expiration = 0;\n\n\t/**\n\t * List of server headers\n\t *\n\t * @var\tarray\n\t */\n\tpublic $headers = array();\n\n\t/**\n\t * List of mime types\n\t *\n\t * @var\tarray\n\t */\n\tpublic $mimes =\tarray();\n\n\t/**\n\t * Mime-type for the current page\n\t *\n\t * @var\tstring\n\t */\n\tprotected $mime_type = 'text/html';\n\n\t/**\n\t * Enable Profiler flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $enable_profiler = FALSE;\n\n\t/**\n\t * php.ini zlib.output_compression flag\n\t *\n\t * @var\tbool\n\t */\n\tprotected $_zlib_oc = FALSE;\n\n\t/**\n\t * CI output compression flag\n\t *\n\t * @var\tbool\n\t */\n\tprotected $_compress_output = FALSE;\n\n\t/**\n\t * List of profiler sections\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_profiler_sections =\tarray();\n\n\t/**\n\t * Parse markers flag\n\t *\n\t * Whether or not to parse variables like {elapsed_time} and {memory_usage}.\n\t *\n\t * @var\tbool\n\t */\n\tpublic $parse_exec_vars = TRUE;\n\n\t/**\n\t * mbstring.func_overload flag\n\t *\n\t * @var\tbool\n\t */\n\tprotected static $func_overload;\n\n\t/**\n\t * Class constructor\n\t *\n\t * Determines whether zLib output compression will be used.\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __construct()\n\t{\n\t\t$this->_zlib_oc = (bool) ini_get('zlib.output_compression');\n\t\t$this->_compress_output = (\n\t\t\t$this->_zlib_oc === FALSE\n\t\t\t&& config_item('compress_output') === TRUE\n\t\t\t&& extension_loaded('zlib')\n\t\t);\n\n\t\tisset(self::$func_overload) OR self::$func_overload = ( ! is_php('8.0') && extension_loaded('mbstring') && @ini_get('mbstring.func_overload'));\n\n\t\t// Get mime types for later\n\t\t$this->mimes =& get_mimes();\n\n\t\tlog_message('info', 'Output Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get Output\n\t *\n\t * Returns the current output string.\n\t *\n\t * @return\tstring\n\t */\n\tpublic function get_output()\n\t{\n\t\treturn $this->final_output;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Output\n\t *\n\t * Sets the output string.\n\t *\n\t * @param\tstring\t$output\tOutput data\n\t * @return\tCI_Output\n\t */\n\tpublic function set_output($output)\n\t{\n\t\t$this->final_output = $output;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Append Output\n\t *\n\t * Appends data onto the output string.\n\t *\n\t * @param\tstring\t$output\tData to append\n\t * @return\tCI_Output\n\t */\n\tpublic function append_output($output)\n\t{\n\t\t$this->final_output .= $output;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Header\n\t *\n\t * Lets you set a server header which will be sent with the final output.\n\t *\n\t * Note: If a file is cached, headers will not be sent.\n\t * @todo\tWe need to figure out how to permit headers to be cached.\n\t *\n\t * @param\tstring\t$header\t\tHeader\n\t * @param\tbool\t$replace\tWhether to replace the old header value, if already set\n\t * @return\tCI_Output\n\t */\n\tpublic function set_header($header, $replace = TRUE)\n\t{\n\t\t// If zlib.output_compression is enabled it will compress the output,\n\t\t// but it will not modify the content-length header to compensate for\n\t\t// the reduction, causing the browser to hang waiting for more data.\n\t\t// We'll just skip content-length in those cases.\n\t\tif ($this->_zlib_oc && strncasecmp($header, 'content-length', 14) === 0)\n\t\t{\n\t\t\treturn $this;\n\t\t}\n\n\t\t$this->headers[] = array($header, $replace);\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Content-Type Header\n\t *\n\t * @param\tstring\t$mime_type\tExtension of the file we're outputting\n\t * @param\tstring\t$charset\tCharacter set (default: NULL)\n\t * @return\tCI_Output\n\t */\n\tpublic function set_content_type($mime_type, $charset = NULL)\n\t{\n\t\tif (strpos($mime_type, '/') === FALSE)\n\t\t{\n\t\t\t$extension = ltrim($mime_type, '.');\n\n\t\t\t// Is this extension supported?\n\t\t\tif (isset($this->mimes[$extension]))\n\t\t\t{\n\t\t\t\t$mime_type =& $this->mimes[$extension];\n\n\t\t\t\tif (is_array($mime_type))\n\t\t\t\t{\n\t\t\t\t\t$mime_type = current($mime_type);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$this->mime_type = $mime_type;\n\n\t\tif (empty($charset))\n\t\t{\n\t\t\t$charset = config_item('charset');\n\t\t}\n\n\t\t$header = 'Content-Type: '.$mime_type\n\t\t\t.(empty($charset) ? '' : '; charset='.$charset);\n\n\t\t$this->headers[] = array($header, TRUE);\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get Current Content-Type Header\n\t *\n\t * @return\tstring\t'text/html', if not already set\n\t */\n\tpublic function get_content_type()\n\t{\n\t\tfor ($i = 0, $c = count($this->headers); $i < $c; $i++)\n\t\t{\n\t\t\tif (sscanf($this->headers[$i][0], 'Content-Type: %[^;]', $content_type) === 1)\n\t\t\t{\n\t\t\t\treturn $content_type;\n\t\t\t}\n\t\t}\n\n\t\treturn 'text/html';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get Header\n\t *\n\t * @param\tstring\t$header\n\t * @return\tstring\n\t */\n\tpublic function get_header($header)\n\t{\n\t\t// We only need [x][0] from our multi-dimensional array\n\t\t$header_lines = array_map(function ($headers)\n\t\t{\n\t\t\treturn array_shift($headers);\n\t\t}, $this->headers);\n\n\t\t$headers = array_merge(\n\t\t\t$header_lines,\n\t\t\theaders_list()\n\t\t);\n\n\t\tif (empty($headers) OR empty($header))\n\t\t{\n\t\t\treturn NULL;\n\t\t}\n\n\t\t// Count backwards, in order to get the last matching header\n\t\tfor ($c = count($headers) - 1; $c > -1; $c--)\n\t\t{\n\t\t\tif (strncasecmp($header, $headers[$c], $l = self::strlen($header)) === 0)\n\t\t\t{\n\t\t\t\treturn trim(self::substr($headers[$c], $l+1));\n\t\t\t}\n\t\t}\n\n\t\treturn NULL;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set HTTP Status Header\n\t *\n\t * As of version 1.7.2, this is an alias for common function\n\t * set_status_header().\n\t *\n\t * @param\tint\t$code\tStatus code (default: 200)\n\t * @param\tstring\t$text\tOptional message\n\t * @return\tCI_Output\n\t */\n\tpublic function set_status_header($code = 200, $text = '')\n\t{\n\t\tset_status_header($code, $text);\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Enable/disable Profiler\n\t *\n\t * @param\tbool\t$val\tTRUE to enable or FALSE to disable\n\t * @return\tCI_Output\n\t */\n\tpublic function enable_profiler($val = TRUE)\n\t{\n\t\t$this->enable_profiler = is_bool($val) ? $val : TRUE;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Profiler Sections\n\t *\n\t * Allows override of default/config settings for\n\t * Profiler section display.\n\t *\n\t * @param\tarray\t$sections\tProfiler sections\n\t * @return\tCI_Output\n\t */\n\tpublic function set_profiler_sections($sections)\n\t{\n\t\tif (isset($sections['query_toggle_count']))\n\t\t{\n\t\t\t$this->_profiler_sections['query_toggle_count'] = (int) $sections['query_toggle_count'];\n\t\t\tunset($sections['query_toggle_count']);\n\t\t}\n\n\t\tforeach ($sections as $section => $enable)\n\t\t{\n\t\t\t$this->_profiler_sections[$section] = ($enable !== FALSE);\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Cache\n\t *\n\t * @param\tint\t$time\tCache expiration time in minutes\n\t * @return\tCI_Output\n\t */\n\tpublic function cache($time)\n\t{\n\t\t$this->cache_expiration = is_numeric($time) ? $time : 0;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Display Output\n\t *\n\t * Processes and sends finalized output data to the browser along\n\t * with any server headers and profile data. It also stops benchmark\n\t * timers so the page rendering speed and memory usage can be shown.\n\t *\n\t * Note: All \"view\" data is automatically put into $this->final_output\n\t *\t by controller class.\n\t *\n\t * @uses\tCI_Output::$final_output\n\t * @param\tstring\t$output\tOutput data override\n\t * @return\tvoid\n\t */\n\tpublic function _display($output = NULL)\n\t{\n\t\t// Note:  We use load_class() because we can't use $CI =& get_instance()\n\t\t// since this function is sometimes called by the caching mechanism,\n\t\t// which happens before the CI super object is available.\n\t\t$BM =& load_class('Benchmark', 'core');\n\t\t$CFG =& load_class('Config', 'core');\n\n\t\t// Grab the super object if we can.\n\t\tif (class_exists('CI_Controller', FALSE))\n\t\t{\n\t\t\t$CI =& get_instance();\n\t\t}\n\n\t\t// --------------------------------------------------------------------\n\n\t\t// Set the output data\n\t\tif ($output === NULL)\n\t\t{\n\t\t\t$output =& $this->final_output;\n\t\t}\n\n\t\t// --------------------------------------------------------------------\n\n\t\t// Do we need to write a cache file? Only if the controller does not have its\n\t\t// own _output() method and we are not dealing with a cache file, which we\n\t\t// can determine by the existence of the $CI object above\n\t\tif ($this->cache_expiration > 0 && isset($CI) && ! method_exists($CI, '_output'))\n\t\t{\n\t\t\t$this->_write_cache($output);\n\t\t}\n\n\t\t// --------------------------------------------------------------------\n\n\t\t// Parse out the elapsed time and memory usage,\n\t\t// then swap the pseudo-variables with the data\n\n\t\t$elapsed = $BM->elapsed_time('total_execution_time_start', 'total_execution_time_end');\n\n\t\tif ($this->parse_exec_vars === TRUE)\n\t\t{\n\t\t\t$memory\t= round(memory_get_usage() / 1024 / 1024, 2).'MB';\n\t\t\t$output = str_replace(array('{elapsed_time}', '{memory_usage}'), array($elapsed, $memory), $output);\n\t\t}\n\n\t\t// --------------------------------------------------------------------\n\n\t\t// Is compression requested?\n\t\tif (isset($CI) // This means that we're not serving a cache file, if we were, it would already be compressed\n\t\t\t&& $this->_compress_output === TRUE\n\t\t\t&& isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)\n\t\t{\n\t\t\tob_start('ob_gzhandler');\n\t\t}\n\n\t\t// --------------------------------------------------------------------\n\n\t\t// Are there any server headers to send?\n\t\tif (count($this->headers) > 0)\n\t\t{\n\t\t\tforeach ($this->headers as $header)\n\t\t\t{\n\t\t\t\t@header($header[0], $header[1]);\n\t\t\t}\n\t\t}\n\n\t\t// --------------------------------------------------------------------\n\n\t\t// Does the $CI object exist?\n\t\t// If not we know we are dealing with a cache file so we'll\n\t\t// simply echo out the data and exit.\n\t\tif ( ! isset($CI))\n\t\t{\n\t\t\tif ($this->_compress_output === TRUE)\n\t\t\t{\n\t\t\t\tif (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)\n\t\t\t\t{\n\t\t\t\t\theader('Content-Encoding: gzip');\n\t\t\t\t\theader('Content-Length: '.self::strlen($output));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// User agent doesn't support gzip compression,\n\t\t\t\t\t// so we'll have to decompress our cache\n\t\t\t\t\t$output = gzinflate(self::substr($output, 10, -8));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\techo $output;\n\t\t\tlog_message('info', 'Final output sent to browser');\n\t\t\tlog_message('info', 'Total execution time: '.$elapsed);\n\t\t\treturn;\n\t\t}\n\n\t\t// --------------------------------------------------------------------\n\n\t\t// Do we need to generate profile data?\n\t\t// If so, load the Profile class and run it.\n\t\tif ($this->enable_profiler === TRUE)\n\t\t{\n\t\t\t$CI->load->library('profiler');\n\t\t\tif ( ! empty($this->_profiler_sections))\n\t\t\t{\n\t\t\t\t$CI->profiler->set_sections($this->_profiler_sections);\n\t\t\t}\n\n\t\t\t// If the output data contains closing </body> and </html> tags\n\t\t\t// we will remove them and add them back after we insert the profile data\n\t\t\t$output = preg_replace('|</body>.*?</html>|is', '', $output, -1, $count).$CI->profiler->run();\n\t\t\tif ($count > 0)\n\t\t\t{\n\t\t\t\t$output .= '</body></html>';\n\t\t\t}\n\t\t}\n\n\t\t// Does the controller contain a function named _output()?\n\t\t// If so send the output there.  Otherwise, echo it.\n\t\tif (method_exists($CI, '_output'))\n\t\t{\n\t\t\t$CI->_output($output);\n\t\t}\n\t\telse\n\t\t{\n\t\t\techo $output; // Send it to the browser!\n\t\t}\n\n\t\tlog_message('info', 'Final output sent to browser');\n\t\tlog_message('info', 'Total execution time: '.$elapsed);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Write Cache\n\t *\n\t * @param\tstring\t$output\tOutput data to cache\n\t * @return\tvoid\n\t */\n\tpublic function _write_cache($output)\n\t{\n\t\t$CI =& get_instance();\n\t\t$path = $CI->config->item('cache_path');\n\t\t$cache_path = ($path === '') ? APPPATH.'cache'.DIRECTORY_SEPARATOR : rtrim($path, '/\\\\').DIRECTORY_SEPARATOR;\n\n\t\tif ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))\n\t\t{\n\t\t\tlog_message('error', 'Unable to write cache file: '.$cache_path);\n\t\t\treturn;\n\t\t}\n\n\t\t$uri = $CI->config->item('base_url')\n\t\t\t.$CI->config->slash_item('index_page')\n\t\t\t.$CI->uri->uri_string();\n\n\t\tif (($cache_query_string = $CI->config->item('cache_query_string')) && ! empty($_SERVER['QUERY_STRING']))\n\t\t{\n\t\t\tif (is_array($cache_query_string))\n\t\t\t{\n\t\t\t\t$uri .= '?'.http_build_query(array_intersect_key($_GET, array_flip($cache_query_string)));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$uri .= '?'.$_SERVER['QUERY_STRING'];\n\t\t\t}\n\t\t}\n\n\t\t$cache_path .= md5($uri);\n\n\t\tif ( ! $fp = @fopen($cache_path, 'w+b'))\n\t\t{\n\t\t\tlog_message('error', 'Unable to write cache file: '.$cache_path);\n\t\t\treturn;\n\t\t}\n\n\t\tif ( ! flock($fp, LOCK_EX))\n\t\t{\n\t\t\tlog_message('error', 'Unable to secure a file lock for file at: '.$cache_path);\n\t\t\tfclose($fp);\n\t\t\treturn;\n\t\t}\n\n\t\t// If output compression is enabled, compress the cache\n\t\t// itself, so that we don't have to do that each time\n\t\t// we're serving it\n\t\tif ($this->_compress_output === TRUE)\n\t\t{\n\t\t\t$output = gzencode($output);\n\n\t\t\tif ($this->get_header('content-type') === NULL)\n\t\t\t{\n\t\t\t\t$this->set_content_type($this->mime_type);\n\t\t\t}\n\t\t}\n\n\t\t$expire = time() + ($this->cache_expiration * 60);\n\n\t\t// Put together our serialized info.\n\t\t$cache_info = serialize(array(\n\t\t\t'expire'\t=> $expire,\n\t\t\t'headers'\t=> $this->headers\n\t\t));\n\n\t\t$output = $cache_info.'ENDCI--->'.$output;\n\n\t\tfor ($written = 0, $length = self::strlen($output); $written < $length; $written += $result)\n\t\t{\n\t\t\tif (($result = fwrite($fp, self::substr($output, $written))) === FALSE)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tflock($fp, LOCK_UN);\n\t\tfclose($fp);\n\n\t\tif ( ! is_int($result))\n\t\t{\n\t\t\t@unlink($cache_path);\n\t\t\tlog_message('error', 'Unable to write the complete cache content at: '.$cache_path);\n\t\t\treturn;\n\t\t}\n\n\t\tchmod($cache_path, 0640);\n\t\tlog_message('debug', 'Cache file written: '.$cache_path);\n\n\t\t// Send HTTP cache-control headers to browser to match file cache settings.\n\t\t$this->set_cache_header($_SERVER['REQUEST_TIME'], $expire);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Update/serve cached output\n\t *\n\t * @uses\tCI_Config\n\t * @uses\tCI_URI\n\t *\n\t * @param\tobject\t&$CFG\tCI_Config class instance\n\t * @param\tobject\t&$URI\tCI_URI class instance\n\t * @return\tbool\tTRUE on success or FALSE on failure\n\t */\n\tpublic function _display_cache(&$CFG, &$URI)\n\t{\n\t\t$cache_path = ($CFG->item('cache_path') === '') ? APPPATH.'cache/' : $CFG->item('cache_path');\n\n\t\t// Build the file path. The file name is an MD5 hash of the full URI\n\t\t$uri = $CFG->item('base_url').$CFG->slash_item('index_page').$URI->uri_string;\n\n\t\tif (($cache_query_string = $CFG->item('cache_query_string')) && ! empty($_SERVER['QUERY_STRING']))\n\t\t{\n\t\t\tif (is_array($cache_query_string))\n\t\t\t{\n\t\t\t\t$uri .= '?'.http_build_query(array_intersect_key($_GET, array_flip($cache_query_string)));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$uri .= '?'.$_SERVER['QUERY_STRING'];\n\t\t\t}\n\t\t}\n\n\t\t$filepath = $cache_path.md5($uri);\n\n\t\tif ( ! file_exists($filepath) OR ! $fp = @fopen($filepath, 'rb'))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tflock($fp, LOCK_SH);\n\n\t\t$cache = (filesize($filepath) > 0) ? fread($fp, filesize($filepath)) : '';\n\n\t\tflock($fp, LOCK_UN);\n\t\tfclose($fp);\n\n\t\t// Look for embedded serialized file info.\n\t\tif ( ! preg_match('/^(.*)ENDCI--->/', $cache, $match))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$cache_info = unserialize($match[1]);\n\t\t$expire = $cache_info['expire'];\n\n\t\t$last_modified = filemtime($filepath);\n\n\t\t// Has the file expired?\n\t\tif ($_SERVER['REQUEST_TIME'] >= $expire && is_really_writable($cache_path))\n\t\t{\n\t\t\t// If so we'll delete it.\n\t\t\t@unlink($filepath);\n\t\t\tlog_message('debug', 'Cache file has expired. File deleted.');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Send the HTTP cache control headers\n\t\t$this->set_cache_header($last_modified, $expire);\n\n\t\t// Add headers from cache file.\n\t\tforeach ($cache_info['headers'] as $header)\n\t\t{\n\t\t\t$this->set_header($header[0], $header[1]);\n\t\t}\n\n\t\t// Display the cache\n\t\t$this->_display(self::substr($cache, self::strlen($match[0])));\n\t\tlog_message('debug', 'Cache file is current. Sending it to browser.');\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Delete cache\n\t *\n\t * @param\tstring\t$uri\tURI string\n\t * @return\tbool\n\t */\n\tpublic function delete_cache($uri = '')\n\t{\n\t\t$CI =& get_instance();\n\t\t$cache_path = $CI->config->item('cache_path');\n\t\tif ($cache_path === '')\n\t\t{\n\t\t\t$cache_path = APPPATH.'cache/';\n\t\t}\n\n\t\tif ( ! is_dir($cache_path))\n\t\t{\n\t\t\tlog_message('error', 'Unable to find cache path: '.$cache_path);\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif (empty($uri))\n\t\t{\n\t\t\t$uri = $CI->uri->uri_string();\n\n\t\t\tif (($cache_query_string = $CI->config->item('cache_query_string')) && ! empty($_SERVER['QUERY_STRING']))\n\t\t\t{\n\t\t\t\tif (is_array($cache_query_string))\n\t\t\t\t{\n\t\t\t\t\t$uri .= '?'.http_build_query(array_intersect_key($_GET, array_flip($cache_query_string)));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$uri .= '?'.$_SERVER['QUERY_STRING'];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$cache_path .= md5($CI->config->item('base_url').$CI->config->slash_item('index_page').ltrim($uri, '/'));\n\n\t\tif ( ! @unlink($cache_path))\n\t\t{\n\t\t\tlog_message('error', 'Unable to delete cache file for '.$uri);\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Cache Header\n\t *\n\t * Set the HTTP headers to match the server-side file cache settings\n\t * in order to reduce bandwidth.\n\t *\n\t * @param\tint\t$last_modified\tTimestamp of when the page was last modified\n\t * @param\tint\t$expiration\tTimestamp of when should the requested page expire from cache\n\t * @return\tvoid\n\t */\n\tpublic function set_cache_header($last_modified, $expiration)\n\t{\n\t\t$max_age = $expiration - $_SERVER['REQUEST_TIME'];\n\n\t\tif (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $last_modified <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']))\n\t\t{\n\t\t\t$this->set_status_header(304);\n\t\t\texit;\n\t\t}\n\n\t\theader('Pragma: public');\n\t\theader('Cache-Control: max-age='.$max_age.', public');\n\t\theader('Expires: '.gmdate('D, d M Y H:i:s', $expiration).' GMT');\n\t\theader('Last-modified: '.gmdate('D, d M Y H:i:s', $last_modified).' GMT');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Byte-safe strlen()\n\t *\n\t * @param\tstring\t$str\n\t * @return\tint\n\t */\n\tprotected static function strlen($str)\n\t{\n\t\treturn (self::$func_overload)\n\t\t\t? mb_strlen($str, '8bit')\n\t\t\t: strlen($str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Byte-safe substr()\n\t *\n\t * @param\tstring\t$str\n\t * @param\tint\t$start\n\t * @param\tint\t$length\n\t * @return\tstring\n\t */\n\tprotected static function substr($str, $start, $length = NULL)\n\t{\n\t\tif (self::$func_overload)\n\t\t{\n\t\t\treturn mb_substr($str, $start, $length, '8bit');\n\t\t}\n\n\t\treturn isset($length)\n\t\t\t? substr($str, $start, $length)\n\t\t\t: substr($str, $start);\n\t}\n}\n"
  },
  {
    "path": "system/core/Router.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Router Class\n *\n * Parses URIs and determines routing\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tLibraries\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/general/routing.html\n */\nclass CI_Router {\n\n\t/**\n\t * CI_Config class object\n\t *\n\t * @var\tobject\n\t */\n\tpublic $config;\n\n\t/**\n\t * List of routes\n\t *\n\t * @var\tarray\n\t */\n\tpublic $routes =\tarray();\n\n\t/**\n\t * Current class name\n\t *\n\t * @var\tstring\n\t */\n\tpublic $class =\t\t'';\n\n\t/**\n\t * Current method name\n\t *\n\t * @var\tstring\n\t */\n\tpublic $method =\t'index';\n\n\t/**\n\t * Sub-directory that contains the requested controller class\n\t *\n\t * @var\tstring\n\t */\n\tpublic $directory = '';\n\n\t/**\n\t * Default controller (and method if specific)\n\t *\n\t * @var\tstring\n\t */\n\tpublic $default_controller;\n\n\t/**\n\t * Translate URI dashes\n\t *\n\t * Determines whether dashes in controller & method segments\n\t * should be automatically replaced by underscores.\n\t *\n\t * @var\tbool\n\t */\n\tpublic $translate_uri_dashes = FALSE;\n\n\t/**\n\t * Enable query strings flag\n\t *\n\t * Determines whether to use GET parameters or segment URIs\n\t *\n\t * @var\tbool\n\t */\n\tpublic $enable_query_strings = FALSE;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * Runs the route mapping function.\n\t *\n\t * @param\tarray\t$routing\n\t * @return\tvoid\n\t */\n\tpublic function __construct($routing = NULL)\n\t{\n\t\t$this->config =& load_class('Config', 'core');\n\t\t$this->uri =& load_class('URI', 'core');\n\n\t\t$this->enable_query_strings = ( ! is_cli() && $this->config->item('enable_query_strings') === TRUE);\n\n\t\t// If a directory override is configured, it has to be set before any dynamic routing logic\n\t\tis_array($routing) && isset($routing['directory']) && $this->set_directory($routing['directory']);\n\t\t$this->_set_routing();\n\n\t\t// Set any routing overrides that may exist in the main index file\n\t\tif (is_array($routing))\n\t\t{\n\t\t\tempty($routing['controller']) OR $this->set_class($routing['controller']);\n\t\t\tempty($routing['function'])   OR $this->set_method($routing['function']);\n\t\t}\n\n\t\tlog_message('info', 'Router Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set route mapping\n\t *\n\t * Determines what should be served based on the URI request,\n\t * as well as any \"routes\" that have been set in the routing config file.\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _set_routing()\n\t{\n\t\t// Load the routes.php file. It would be great if we could\n\t\t// skip this for enable_query_strings = TRUE, but then\n\t\t// default_controller would be empty ...\n\t\tif (file_exists(APPPATH.'config/routes.php'))\n\t\t{\n\t\t\tinclude(APPPATH.'config/routes.php');\n\t\t}\n\n\t\tif (file_exists(APPPATH.'config/'.ENVIRONMENT.'/routes.php'))\n\t\t{\n\t\t\tinclude(APPPATH.'config/'.ENVIRONMENT.'/routes.php');\n\t\t}\n\n\t\t// Validate & get reserved routes\n\t\tif (isset($route) && is_array($route))\n\t\t{\n\t\t\tisset($route['default_controller']) && $this->default_controller = $route['default_controller'];\n\t\t\tisset($route['translate_uri_dashes']) && $this->translate_uri_dashes = $route['translate_uri_dashes'];\n\t\t\tunset($route['default_controller'], $route['translate_uri_dashes']);\n\t\t\t$this->routes = $route;\n\t\t}\n\n\t\t// Are query strings enabled in the config file? Normally CI doesn't utilize query strings\n\t\t// since URI segments are more search-engine friendly, but they can optionally be used.\n\t\t// If this feature is enabled, we will gather the directory/class/method a little differently\n\t\tif ($this->enable_query_strings)\n\t\t{\n\t\t\t// If the directory is set at this time, it means an override exists, so skip the checks\n\t\t\tif ( ! isset($this->directory))\n\t\t\t{\n\t\t\t\t$_d = $this->config->item('directory_trigger');\n\t\t\t\t$_d = isset($_GET[$_d]) ? trim($_GET[$_d], \" \\t\\n\\r\\0\\x0B/\") : '';\n\n\t\t\t\tif ($_d !== '')\n\t\t\t\t{\n\t\t\t\t\t$this->uri->filter_uri($_d);\n\t\t\t\t\t$this->set_directory($_d);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$_c = trim($this->config->item('controller_trigger'));\n\t\t\tif ( ! empty($_GET[$_c]))\n\t\t\t{\n\t\t\t\t$this->uri->filter_uri($_GET[$_c]);\n\t\t\t\t$this->set_class($_GET[$_c]);\n\n\t\t\t\t$_f = trim($this->config->item('function_trigger'));\n\t\t\t\tif ( ! empty($_GET[$_f]))\n\t\t\t\t{\n\t\t\t\t\t$this->uri->filter_uri($_GET[$_f]);\n\t\t\t\t\t$this->set_method($_GET[$_f]);\n\t\t\t\t}\n\n\t\t\t\t$this->uri->rsegments = array(\n\t\t\t\t\t1 => $this->class,\n\t\t\t\t\t2 => $this->method\n\t\t\t\t);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$this->_set_default_controller();\n\t\t\t}\n\n\t\t\t// Routing rules don't apply to query strings and we don't need to detect\n\t\t\t// directories, so we're done here\n\t\t\treturn;\n\t\t}\n\n\t\t// Is there anything to parse?\n\t\tif ($this->uri->uri_string !== '')\n\t\t{\n\t\t\t$this->_parse_routes();\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$this->_set_default_controller();\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set request route\n\t *\n\t * Takes an array of URI segments as input and sets the class/method\n\t * to be called.\n\t *\n\t * @used-by\tCI_Router::_parse_routes()\n\t * @param\tarray\t$segments\tURI segments\n\t * @return\tvoid\n\t */\n\tprotected function _set_request($segments = array())\n\t{\n\t\t$segments = $this->_validate_request($segments);\n\t\t// If we don't have any segments left - try the default controller;\n\t\t// WARNING: Directories get shifted out of the segments array!\n\t\tif (empty($segments))\n\t\t{\n\t\t\t$this->_set_default_controller();\n\t\t\treturn;\n\t\t}\n\n\t\tif ($this->translate_uri_dashes === TRUE)\n\t\t{\n\t\t\t$segments[0] = str_replace('-', '_', $segments[0]);\n\t\t\tif (isset($segments[1]))\n\t\t\t{\n\t\t\t\t$segments[1] = str_replace('-', '_', $segments[1]);\n\t\t\t}\n\t\t}\n\n\t\t$this->set_class($segments[0]);\n\t\tif (isset($segments[1]))\n\t\t{\n\t\t\t$this->set_method($segments[1]);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$segments[1] = 'index';\n\t\t}\n\n\t\tarray_unshift($segments, NULL);\n\t\tunset($segments[0]);\n\t\t$this->uri->rsegments = $segments;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set default controller\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _set_default_controller()\n\t{\n\t\tif (empty($this->default_controller))\n\t\t{\n\t\t\tshow_error('Unable to determine what should be displayed. A default route has not been specified in the routing file.');\n\t\t}\n\n\t\t// Is the method being specified?\n\t\tif (sscanf($this->default_controller, '%[^/]/%s', $class, $method) !== 2)\n\t\t{\n\t\t\t$method = 'index';\n\t\t}\n\n\t\tif ( ! file_exists(APPPATH.'controllers/'.$this->directory.ucfirst($class).'.php'))\n\t\t{\n\t\t\t// This will trigger 404 later\n\t\t\treturn;\n\t\t}\n\n\t\t$this->set_class($class);\n\t\t$this->set_method($method);\n\n\t\t// Assign routed segments, index starting from 1\n\t\t$this->uri->rsegments = array(\n\t\t\t1 => $class,\n\t\t\t2 => $method\n\t\t);\n\n\t\tlog_message('debug', 'No URI present. Default controller set.');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Validate request\n\t *\n\t * Attempts validate the URI request and determine the controller path.\n\t *\n\t * @used-by\tCI_Router::_set_request()\n\t * @param\tarray\t$segments\tURI segments\n\t * @return\tmixed\tURI segments\n\t */\n\tprotected function _validate_request($segments)\n\t{\n\t\t$c = count($segments);\n\t\t$directory_override = $this->directory !== '';\n\n\t\t// Loop through our segments and return as soon as a controller\n\t\t// is found or when such a directory doesn't exist\n\t\twhile ($c-- > 0)\n\t\t{\n\t\t\t$test = $this->directory\n\t\t\t\t.ucfirst($this->translate_uri_dashes === TRUE ? str_replace('-', '_', $segments[0]) : $segments[0]);\n\n\t\t\tif ( ! file_exists(APPPATH.'controllers/'.$test.'.php')\n\t\t\t\t&& $directory_override === FALSE\n\t\t\t\t&& is_dir(APPPATH.'controllers/'.$this->directory.$segments[0])\n\t\t\t)\n\t\t\t{\n\t\t\t\t$this->set_directory(array_shift($segments), TRUE);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\treturn $segments;\n\t\t}\n\n\t\t// This means that all segments were actually directories\n\t\treturn $segments;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Parse Routes\n\t *\n\t * Matches any routes that may exist in the config/routes.php file\n\t * against the URI to determine if the class/method need to be remapped.\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _parse_routes()\n\t{\n\t\t// Turn the segment array into a URI string\n\t\t$uri = implode('/', $this->uri->segments);\n\n\t\t// Get HTTP verb\n\t\t$http_verb = isset($_SERVER['REQUEST_METHOD']) ? strtolower($_SERVER['REQUEST_METHOD']) : 'cli';\n\n\t\t// Loop through the route array looking for wildcards\n\t\tforeach ($this->routes as $key => $val)\n\t\t{\n\t\t\t// Check if route format is using HTTP verbs\n\t\t\tif (is_array($val))\n\t\t\t{\n\t\t\t\t$val = array_change_key_case($val, CASE_LOWER);\n\t\t\t\tif (isset($val[$http_verb]))\n\t\t\t\t{\n\t\t\t\t\t$val = $val[$http_verb];\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Convert wildcards to RegEx\n\t\t\t$key = str_replace(array(':any', ':num'), array('[^/]+', '[0-9]+'), $key);\n\n\t\t\t// Does the RegEx match?\n\t\t\tif (preg_match('#^'.$key.'$#', $uri, $matches))\n\t\t\t{\n\t\t\t\t// Are we using callbacks to process back-references?\n\t\t\t\tif ( ! is_string($val) && is_callable($val))\n\t\t\t\t{\n\t\t\t\t\t// Remove the original string from the matches array.\n\t\t\t\t\tarray_shift($matches);\n\n\t\t\t\t\t// Execute the callback using the values in matches as its parameters.\n\t\t\t\t\t$val = call_user_func_array($val, $matches);\n\t\t\t\t}\n\t\t\t\t// Are we using the default routing method for back-references?\n\t\t\t\telseif (strpos($val, '$') !== FALSE && strpos($key, '(') !== FALSE)\n\t\t\t\t{\n\t\t\t\t\t$val = preg_replace('#^'.$key.'$#', $val, $uri);\n\t\t\t\t}\n\n\t\t\t\t$this->_set_request(explode('/', $val));\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// If we got this far it means we didn't encounter a\n\t\t// matching route so we'll set the site default route\n\t\t$this->_set_request(array_values($this->uri->segments));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set class name\n\t *\n\t * @param\tstring\t$class\tClass name\n\t * @return\tvoid\n\t */\n\tpublic function set_class($class)\n\t{\n\t\t$this->class = str_replace(array('/', '.'), '', $class);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set method name\n\t *\n\t * @param\tstring\t$method\tMethod name\n\t * @return\tvoid\n\t */\n\tpublic function set_method($method)\n\t{\n\t\t$this->method = $method;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set directory name\n\t *\n\t * @param\tstring\t$dir\tDirectory name\n\t * @param\tbool\t$append\tWhether we're appending rather than setting the full value\n\t * @return\tvoid\n\t */\n\tpublic function set_directory($dir, $append = FALSE)\n\t{\n\t\tif ($append !== TRUE OR empty($this->directory))\n\t\t{\n\t\t\t$this->directory = str_replace('.', '', trim($dir, '/')).'/';\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$this->directory .= str_replace('.', '', trim($dir, '/')).'/';\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "system/core/Security.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Security Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tSecurity\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/security.html\n */\nclass CI_Security {\n\n\t/**\n\t * List of sanitize filename strings\n\t *\n\t * @var\tarray\n\t */\n\tpublic $filename_bad_chars =\tarray(\n\t\t'../', '<!--', '-->', '<', '>',\n\t\t\"'\", '\"', '&', '$', '#',\n\t\t'{', '}', '[', ']', '=',\n\t\t';', '?', '%20', '%22',\n\t\t'%3c',\t\t// <\n\t\t'%253c',\t// <\n\t\t'%3e',\t\t// >\n\t\t'%0e',\t\t// >\n\t\t'%28',\t\t// (\n\t\t'%29',\t\t// )\n\t\t'%2528',\t// (\n\t\t'%26',\t\t// &\n\t\t'%24',\t\t// $\n\t\t'%3f',\t\t// ?\n\t\t'%3b',\t\t// ;\n\t\t'%3d'\t\t// =\n\t);\n\n\t/**\n\t * Character set\n\t *\n\t * Will be overridden by the constructor.\n\t *\n\t * @var\tstring\n\t */\n\tpublic $charset = 'UTF-8';\n\n\t/**\n\t * XSS Hash\n\t *\n\t * Random Hash for protecting URLs.\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_xss_hash;\n\n\t/**\n\t * CSRF Hash\n\t *\n\t * Random hash for Cross Site Request Forgery protection cookie\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_csrf_hash;\n\n\t/**\n\t * CSRF Expire time\n\t *\n\t * Expiration time for Cross Site Request Forgery protection cookie.\n\t * Defaults to two hours (in seconds).\n\t *\n\t * @var\tint\n\t */\n\tprotected $_csrf_expire =\t7200;\n\n\t/**\n\t * CSRF Token name\n\t *\n\t * Token name for Cross Site Request Forgery protection cookie.\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_csrf_token_name =\t'ci_csrf_token';\n\n\t/**\n\t * CSRF Cookie name\n\t *\n\t * Cookie name for Cross Site Request Forgery protection cookie.\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_csrf_cookie_name =\t'ci_csrf_token';\n\n\t/**\n\t * List of never allowed strings\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_never_allowed_str =\tarray(\n\t\t'document.cookie' => '[removed]',\n\t\t'(document).cookie' => '[removed]',\n\t\t'document.write'  => '[removed]',\n\t\t'(document).write'  => '[removed]',\n\t\t'.parentNode'     => '[removed]',\n\t\t'.innerHTML'      => '[removed]',\n\t\t'-moz-binding'    => '[removed]',\n\t\t'<!--'            => '&lt;!--',\n\t\t'-->'             => '--&gt;',\n\t\t'<![CDATA['       => '&lt;![CDATA[',\n\t\t'<comment>'\t  => '&lt;comment&gt;',\n\t\t'<%'              => '&lt;&#37;'\n\t);\n\n\t/**\n\t * List of never allowed regex replacements\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_never_allowed_regex = array(\n\t\t'javascript\\s*:',\n\t\t'(\\(?document\\)?|\\(?window\\)?(\\.document)?)\\.(location|on\\w*)',\n\t\t'expression\\s*(\\(|&\\#40;)', // CSS and IE\n\t\t'vbscript\\s*:', // IE, surprise!\n\t\t'wscript\\s*:', // IE\n\t\t'jscript\\s*:', // IE\n\t\t'vbs\\s*:', // IE\n\t\t'Redirect\\s+30\\d',\n\t\t\"([\\\"'])?data\\s*:[^\\\\1]*?base64[^\\\\1]*?,[^\\\\1]*?\\\\1?\"\n\t);\n\n\t/**\n\t * Class constructor\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __construct($charset)\n\t{\n\t\t$this->charset = $charset;\n\n\t\t// Is CSRF protection enabled?\n\t\tif (config_item('csrf_protection') && ! is_cli())\n\t\t{\n\t\t\t// CSRF config\n\t\t\tforeach (array('csrf_expire', 'csrf_token_name', 'csrf_cookie_name') as $key)\n\t\t\t{\n\t\t\t\tif (NULL !== ($val = config_item($key)))\n\t\t\t\t{\n\t\t\t\t\t$this->{'_'.$key} = $val;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Append application specific cookie prefix\n\t\t\tif ($cookie_prefix = config_item('cookie_prefix'))\n\t\t\t{\n\t\t\t\t$this->_csrf_cookie_name = $cookie_prefix.$this->_csrf_cookie_name;\n\t\t\t}\n\n\t\t\t// Set the CSRF hash\n\t\t\t$this->_csrf_set_hash();\n\t\t\t$this->csrf_verify();\n\t\t}\n\n\t\tlog_message('info', 'Security Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * CSRF Verify\n\t *\n\t * @return\tCI_Security\n\t */\n\tpublic function csrf_verify()\n\t{\n\t\t// If it's not a POST request we will set the CSRF cookie\n\t\tif (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST')\n\t\t{\n\t\t\treturn $this->csrf_set_cookie();\n\t\t}\n\n\t\t// Check if URI has been whitelisted from CSRF checks\n\t\tif ($exclude_uris = config_item('csrf_exclude_uris'))\n\t\t{\n\t\t\t$uri = load_class('URI', 'core');\n\t\t\tforeach ($exclude_uris as $excluded)\n\t\t\t{\n\t\t\t\tif (preg_match('#^'.$excluded.'$#i'.(UTF8_ENABLED ? 'u' : ''), $uri->uri_string()))\n\t\t\t\t{\n\t\t\t\t\treturn $this;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Check CSRF token validity, but don't error on mismatch just yet - we'll want to regenerate\n\t\t$valid = isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name])\n\t\t\t&& is_string($_POST[$this->_csrf_token_name]) && is_string($_COOKIE[$this->_csrf_cookie_name])\n\t\t\t&& hash_equals($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name]);\n\n\t\t// We kill this since we're done and we don't want to pollute the _POST array\n\t\tunset($_POST[$this->_csrf_token_name]);\n\n\t\t// Regenerate on every submission?\n\t\tif (config_item('csrf_regenerate'))\n\t\t{\n\t\t\t// Nothing should last forever\n\t\t\tunset($_COOKIE[$this->_csrf_cookie_name]);\n\t\t\t$this->_csrf_hash = NULL;\n\t\t}\n\n\t\t$this->_csrf_set_hash();\n\t\t$this->csrf_set_cookie();\n\n\t\tif ($valid !== TRUE)\n\t\t{\n\t\t\t$this->csrf_show_error();\n\t\t}\n\n\t\tlog_message('info', 'CSRF token verified');\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * CSRF Set Cookie\n\t *\n\t * @codeCoverageIgnore\n\t * @return\tCI_Security\n\t */\n\tpublic function csrf_set_cookie()\n\t{\n\t\t$expire = time() + $this->_csrf_expire;\n\t\t$secure_cookie = (bool) config_item('cookie_secure');\n\n\t\tif ($secure_cookie && ! is_https())\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif (is_php('7.3'))\n\t\t{\n\t\t\tsetcookie(\n\t\t\t\t$this->_csrf_cookie_name,\n\t\t\t\t$this->_csrf_hash,\n\t\t\t\tarray(\n\t\t\t\t\t'expires'  => $expire,\n\t\t\t\t\t'path'     => config_item('cookie_path'),\n\t\t\t\t\t'domain'   => config_item('cookie_domain'),\n\t\t\t\t\t'secure'   => $secure_cookie,\n\t\t\t\t\t'httponly' => config_item('cookie_httponly'),\n\t\t\t\t\t'samesite' => 'Strict'\n\t\t\t\t)\n\t\t\t);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$domain = trim(config_item('cookie_domain'));\n\t\t\theader('Set-Cookie: '.$this->_csrf_cookie_name.'='.$this->_csrf_hash\n\t\t\t\t\t.'; Expires='.gmdate('D, d-M-Y H:i:s T', $expire)\n\t\t\t\t\t.'; Max-Age='.$this->_csrf_expire\n\t\t\t\t\t.'; Path='.implode('/', array_map('rawurlencode', explode('/', config_item('cookie_path'))))\n\t\t\t\t\t.($domain === '' ? '' : '; Domain='.$domain)\n\t\t\t\t\t.($secure_cookie ? '; Secure' : '')\n\t\t\t\t\t.(config_item('cookie_httponly') ? '; HttpOnly' : '')\n\t\t\t\t\t.'; SameSite=Strict'\n\t\t\t);\n\t\t}\n\n\t\tlog_message('info', 'CSRF cookie sent');\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show CSRF Error\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function csrf_show_error()\n\t{\n\t\tshow_error('The action you have requested is not allowed.', 403);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get CSRF Hash\n\t *\n\t * @see\t\tCI_Security::$_csrf_hash\n\t * @return \tstring\tCSRF hash\n\t */\n\tpublic function get_csrf_hash()\n\t{\n\t\treturn $this->_csrf_hash;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get CSRF Token Name\n\t *\n\t * @see\t\tCI_Security::$_csrf_token_name\n\t * @return\tstring\tCSRF token name\n\t */\n\tpublic function get_csrf_token_name()\n\t{\n\t\treturn $this->_csrf_token_name;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * XSS Clean\n\t *\n\t * Sanitizes data so that Cross Site Scripting Hacks can be\n\t * prevented.  This method does a fair amount of work but\n\t * it is extremely thorough, designed to prevent even the\n\t * most obscure XSS attempts.  Nothing is ever 100% foolproof,\n\t * of course, but I haven't been able to get anything passed\n\t * the filter.\n\t *\n\t * Note: Should only be used to deal with data upon submission.\n\t *\t It's not something that should be used for general\n\t *\t runtime processing.\n\t *\n\t * @link\thttp://channel.bitflux.ch/wiki/XSS_Prevention\n\t * \t\tBased in part on some code and ideas from Bitflux.\n\t *\n\t * @link\thttp://ha.ckers.org/xss.html\n\t * \t\tTo help develop this script I used this great list of\n\t *\t\tvulnerabilities along with a few other hacks I've\n\t *\t\tharvested from examining vulnerabilities in other programs.\n\t *\n\t * @param\tstring|string[]\t$str\t\tInput data\n\t * @param \tbool\t\t$is_image\tWhether the input is an image\n\t * @return\tstring\n\t */\n\tpublic function xss_clean($str, $is_image = FALSE)\n\t{\n\t\t// Is the string an array?\n\t\tif (is_array($str))\n\t\t{\n\t\t\tforeach ($str as $key => &$value)\n\t\t\t{\n\t\t\t\t$str[$key] = $this->xss_clean($value);\n\t\t\t}\n\n\t\t\treturn $str;\n\t\t}\n\n\t\t// Remove Invisible Characters\n\t\t$str = remove_invisible_characters($str);\n\n\t\t/*\n\t\t * URL Decode\n\t\t *\n\t\t * Just in case stuff like this is submitted:\n\t\t *\n\t\t * <a href=\"http://%77%77%77%2E%67%6F%6F%67%6C%65%2E%63%6F%6D\">Google</a>\n\t\t *\n\t\t * Note: Use rawurldecode() so it does not remove plus signs\n\t\t */\n\t\tif (stripos($str, '%') !== false)\n\t\t{\n\t\t\tdo\n\t\t\t{\n\t\t\t\t$oldstr = $str;\n\t\t\t\t$str = rawurldecode($str);\n\t\t\t\t$str = preg_replace_callback('#%(?:\\s*[0-9a-f]){2,}#i', array($this, '_urldecodespaces'), $str);\n\t\t\t}\n\t\t\twhile ($oldstr !== $str);\n\t\t\tunset($oldstr);\n\t\t}\n\n\t\t/*\n\t\t * Convert character entities to ASCII\n\t\t *\n\t\t * This permits our tests below to work reliably.\n\t\t * We only convert entities that are within tags since\n\t\t * these are the ones that will pose security problems.\n\t\t */\n\t\t$str = preg_replace_callback(\"/[^a-z0-9>]+[a-z0-9]+=([\\'\\\"]).*?\\\\1/si\", array($this, '_convert_attribute'), $str);\n\t\t$str = preg_replace_callback('/<\\w+.*/si', array($this, '_decode_entity'), $str);\n\n\t\t// Remove Invisible Characters Again!\n\t\t$str = remove_invisible_characters($str);\n\n\t\t/*\n\t\t * Convert all tabs to spaces\n\t\t *\n\t\t * This prevents strings like this: ja\tvascript\n\t\t * NOTE: we deal with spaces between characters later.\n\t\t * NOTE: preg_replace was found to be amazingly slow here on\n\t\t * large blocks of data, so we use str_replace.\n\t\t */\n\t\t$str = str_replace(\"\\t\", ' ', $str);\n\n\t\t// Capture converted string for later comparison\n\t\t$converted_string = $str;\n\n\t\t// Remove Strings that are never allowed\n\t\t$str = $this->_do_never_allowed($str);\n\n\t\t/*\n\t\t * Makes PHP tags safe\n\t\t *\n\t\t * Note: XML tags are inadvertently replaced too:\n\t\t *\n\t\t * <?xml\n\t\t *\n\t\t * But it doesn't seem to pose a problem.\n\t\t */\n\t\tif ($is_image === TRUE)\n\t\t{\n\t\t\t// Images have a tendency to have the PHP short opening and\n\t\t\t// closing tags every so often so we skip those and only\n\t\t\t// do the long opening tags.\n\t\t\t$str = preg_replace('/<\\?(php)/i', '&lt;?\\\\1', $str);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$str = str_replace(array('<?', '?'.'>'), array('&lt;?', '?&gt;'), $str);\n\t\t}\n\n\t\t/*\n\t\t * Compact any exploded words\n\t\t *\n\t\t * This corrects words like:  j a v a s c r i p t\n\t\t * These words are compacted back to their correct state.\n\t\t */\n\t\t$words = array(\n\t\t\t'javascript', 'expression', 'vbscript', 'jscript', 'wscript',\n\t\t\t'vbs', 'script', 'base64', 'applet', 'alert', 'document',\n\t\t\t'write', 'cookie', 'window', 'confirm', 'prompt', 'eval'\n\t\t);\n\n\t\tforeach ($words as $word)\n\t\t{\n\t\t\t$word = implode('\\s*', str_split($word)).'\\s*';\n\n\t\t\t// We only want to do this when it is followed by a non-word character\n\t\t\t// That way valid stuff like \"dealer to\" does not become \"dealerto\"\n\t\t\t$str = preg_replace_callback('#('.substr($word, 0, -3).')(\\W)#is', array($this, '_compact_exploded_words'), $str);\n\t\t}\n\n\t\t/*\n\t\t * Remove disallowed Javascript in links or img tags\n\t\t * We used to do some version comparisons and use of stripos(),\n\t\t * but it is dog slow compared to these simplified non-capturing\n\t\t * preg_match(), especially if the pattern exists in the string\n\t\t *\n\t\t * Note: It was reported that not only space characters, but all in\n\t\t * the following pattern can be parsed as separators between a tag name\n\t\t * and its attributes: [\\d\\s\"\\'`;,\\/\\=\\(\\x00\\x0B\\x09\\x0C]\n\t\t * ... however, remove_invisible_characters() above already strips the\n\t\t * hex-encoded ones, so we'll skip them below.\n\t\t */\n\t\tdo\n\t\t{\n\t\t\t$original = $str;\n\n\t\t\tif (preg_match('/<a/i', $str))\n\t\t\t{\n\t\t\t\t$str = preg_replace_callback('#<a(?:rea)?[^a-z0-9>]+([^>]*?)(?:>|$)#si', array($this, '_js_link_removal'), $str);\n\t\t\t}\n\n\t\t\tif (preg_match('/<img/i', $str))\n\t\t\t{\n\t\t\t\t$str = preg_replace_callback('#<img[^a-z0-9]+([^>]*?)(?:\\s?/?>|$)#si', array($this, '_js_img_removal'), $str);\n\t\t\t}\n\n\t\t\tif (preg_match('/script|xss/i', $str))\n\t\t\t{\n\t\t\t\t$str = preg_replace('#</*(?:script|xss).*?>#si', '[removed]', $str);\n\t\t\t}\n\t\t}\n\t\twhile ($original !== $str);\n\t\tunset($original);\n\n\t\t/*\n\t\t * Sanitize naughty HTML elements\n\t\t *\n\t\t * If a tag containing any of the words in the list\n\t\t * below is found, the tag gets converted to entities.\n\t\t *\n\t\t * So this: <blink>\n\t\t * Becomes: &lt;blink&gt;\n\t\t */\n\t\t$pattern = '#'\n\t\t\t.'<((?<slash>/*\\s*)((?<tagName>[a-z0-9]+)(?=[^a-z0-9]|$)|.+)' // tag start and name, followed by a non-tag character\n\t\t\t.'[^\\s\\042\\047a-z0-9>/=]*' // a valid attribute character immediately after the tag would count as a separator\n\t\t\t// optional attributes\n\t\t\t.'(?<attributes>(?:[\\s\\042\\047/=]*' // non-attribute characters, excluding > (tag close) for obvious reasons\n\t\t\t.'[^\\s\\042\\047>/=]+' // attribute characters\n\t\t\t// optional attribute-value\n\t\t\t\t.'(?:\\s*=' // attribute-value separator\n\t\t\t\t\t.'(?:[^\\s\\042\\047=><`]+|\\s*\\042[^\\042]*\\042|\\s*\\047[^\\047]*\\047|\\s*(?U:[^\\s\\042\\047=><`]*))' // single, double or non-quoted value\n\t\t\t\t.')?' // end optional attribute-value group\n\t\t\t.')*)' // end optional attributes group\n\t\t\t.'[^>]*)(?<closeTag>\\>)?#isS';\n\n\t\t// Note: It would be nice to optimize this for speed, BUT\n\t\t//       only matching the naughty elements here results in\n\t\t//       false positives and in turn - vulnerabilities!\n\t\tdo\n\t\t{\n\t\t\t$old_str = $str;\n\t\t\t$str = preg_replace_callback($pattern, array($this, '_sanitize_naughty_html'), $str);\n\t\t}\n\t\twhile ($old_str !== $str);\n\t\tunset($old_str);\n\n\t\t/*\n\t\t * Sanitize naughty scripting elements\n\t\t *\n\t\t * Similar to above, only instead of looking for\n\t\t * tags it looks for PHP and JavaScript commands\n\t\t * that are disallowed. Rather than removing the\n\t\t * code, it simply converts the parenthesis to entities\n\t\t * rendering the code un-executable.\n\t\t *\n\t\t * For example:\teval('some code')\n\t\t * Becomes:\teval&#40;'some code'&#41;\n\t\t */\n\t\t$str = preg_replace(\n\t\t\t'#(alert|prompt|confirm|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\\s*)\\((.*?)\\)#si',\n\t\t\t'\\\\1\\\\2&#40;\\\\3&#41;',\n\t\t\t$str\n\t\t);\n\n\t\t// Same thing, but for \"tag functions\" (e.g. eval`some code`)\n\t\t// See https://github.com/bcit-ci/CodeIgniter/issues/5420\n\t\t$str = preg_replace(\n\t\t\t'#(alert|prompt|confirm|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\\s*)`(.*?)`#si',\n\t\t\t'\\\\1\\\\2&#96;\\\\3&#96;',\n\t\t\t$str\n\t\t);\n\n\t\t// Final clean up\n\t\t// This adds a bit of extra precaution in case\n\t\t// something got through the above filters\n\t\t$str = $this->_do_never_allowed($str);\n\n\t\t/*\n\t\t * Images are Handled in a Special Way\n\t\t * - Essentially, we want to know that after all of the character\n\t\t * conversion is done whether any unwanted, likely XSS, code was found.\n\t\t * If not, we return TRUE, as the image is clean.\n\t\t * However, if the string post-conversion does not matched the\n\t\t * string post-removal of XSS, then it fails, as there was unwanted XSS\n\t\t * code found and removed/changed during processing.\n\t\t */\n\t\tif ($is_image === TRUE)\n\t\t{\n\t\t\treturn ($str === $converted_string);\n\t\t}\n\n\t\treturn $str;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * XSS Hash\n\t *\n\t * Generates the XSS hash if needed and returns it.\n\t *\n\t * @see\t\tCI_Security::$_xss_hash\n\t * @return\tstring\tXSS hash\n\t */\n\tpublic function xss_hash()\n\t{\n\t\tif ($this->_xss_hash === NULL)\n\t\t{\n\t\t\t$rand = $this->get_random_bytes(16);\n\t\t\t$this->_xss_hash = ($rand === FALSE)\n\t\t\t\t? md5(uniqid(mt_rand(), TRUE))\n\t\t\t\t: bin2hex($rand);\n\t\t}\n\n\t\treturn $this->_xss_hash;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get random bytes\n\t *\n\t * @param\tint\t$length\tOutput length\n\t * @return\tstring\n\t */\n\tpublic function get_random_bytes($length)\n\t{\n\t\tif (empty($length) OR ! ctype_digit((string) $length))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif (function_exists('random_bytes'))\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\t// The cast is required to avoid TypeError\n\t\t\t\treturn random_bytes((int) $length);\n\t\t\t}\n\t\t\tcatch (Exception $e)\n\t\t\t{\n\t\t\t\t// If random_bytes() can't do the job, we can't either ...\n\t\t\t\t// There's no point in using fallbacks.\n\t\t\t\tlog_message('error', $e->getMessage());\n\t\t\t\treturn FALSE;\n\t\t\t}\n\t\t}\n\n\t\t// Unfortunately, none of the following PRNGs is guaranteed to exist ...\n\t\tif (defined('MCRYPT_DEV_URANDOM') && ($output = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM)) !== FALSE)\n\t\t{\n\t\t\treturn $output;\n\t\t}\n\n\t\tif (is_readable('/dev/urandom') && ($fp = fopen('/dev/urandom', 'rb')) !== FALSE)\n\t\t{\n\t\t\t// Try not to waste entropy ...\n\t\t\tstream_set_chunk_size($fp, $length);\n\t\t\t$output = fread($fp, $length);\n\t\t\tfclose($fp);\n\t\t\tif ($output !== FALSE)\n\t\t\t{\n\t\t\t\treturn $output;\n\t\t\t}\n\t\t}\n\n\t\tif (function_exists('openssl_random_pseudo_bytes'))\n\t\t{\n\t\t\treturn openssl_random_pseudo_bytes($length);\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * HTML Entities Decode\n\t *\n\t * A replacement for html_entity_decode()\n\t *\n\t * The reason we are not using html_entity_decode() by itself is because\n\t * while it is not technically correct to leave out the semicolon\n\t * at the end of an entity most browsers will still interpret the entity\n\t * correctly. html_entity_decode() does not convert entities without\n\t * semicolons, so we are left with our own little solution here. Bummer.\n\t *\n\t * @link\thttps://secure.php.net/html-entity-decode\n\t *\n\t * @param\tstring\t$str\t\tInput\n\t * @param\tstring\t$charset\tCharacter set\n\t * @return\tstring\n\t */\n\tpublic function entity_decode($str, $charset = NULL)\n\t{\n\t\tif (strpos($str, '&') === FALSE)\n\t\t{\n\t\t\treturn $str;\n\t\t}\n\n\t\tstatic $_entities;\n\n\t\tisset($charset)   OR $charset = $this->charset;\n\t\tisset($_entities) OR $_entities = array_map('strtolower', get_html_translation_table(HTML_ENTITIES, ENT_COMPAT | ENT_HTML5, $charset));\n\n\t\tdo\n\t\t{\n\t\t\t$str_compare = $str;\n\n\t\t\t// Decode standard entities, avoiding false positives\n\t\t\tif (preg_match_all('/&[a-z]{2,}(?![a-z;])/i', $str, $matches))\n\t\t\t{\n\t\t\t\t$replace = array();\n\t\t\t\t$matches = array_unique(array_map('strtolower', $matches[0]));\n\t\t\t\tforeach ($matches as &$match)\n\t\t\t\t{\n\t\t\t\t\tif (($char = array_search($match.';', $_entities, TRUE)) !== FALSE)\n\t\t\t\t\t{\n\t\t\t\t\t\t$replace[$match] = $char;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$str = str_replace(array_keys($replace), array_values($replace), $str);\n\t\t\t}\n\n\t\t\t// Decode numeric & UTF16 two byte entities\n\t\t\t$str = html_entity_decode(\n\t\t\t\tpreg_replace('/(&#(?:x0*[0-9a-f]{2,5}(?![0-9a-f;])|(?:0*\\d{2,4}(?![0-9;]))))/iS', '$1;', $str),\n\t\t\t\tENT_COMPAT | ENT_HTML5,\n\t\t\t\t$charset\n\t\t\t);\n\t\t}\n\t\twhile ($str_compare !== $str);\n\t\treturn $str;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Sanitize Filename\n\t *\n\t * @param\tstring\t$str\t\tInput file name\n\t * @param \tbool\t$relative_path\tWhether to preserve paths\n\t * @return\tstring\n\t */\n\tpublic function sanitize_filename($str, $relative_path = FALSE)\n\t{\n\t\t$bad = $this->filename_bad_chars;\n\n\t\tif ( ! $relative_path)\n\t\t{\n\t\t\t$bad[] = './';\n\t\t\t$bad[] = '/';\n\t\t}\n\n\t\t$str = remove_invisible_characters($str, FALSE);\n\n\t\tdo\n\t\t{\n\t\t\t$old = $str;\n\t\t\t$str = str_replace($bad, '', $str);\n\t\t}\n\t\twhile ($old !== $str);\n\n\t\treturn stripslashes($str);\n\t}\n\n\t// ----------------------------------------------------------------\n\n\t/**\n\t * Strip Image Tags\n\t *\n\t * @param\tstring\t$str\n\t * @return\tstring\n\t */\n\tpublic function strip_image_tags($str)\n\t{\n\t\treturn preg_replace(\n\t\t\tarray(\n\t\t\t\t'#<img[\\s/]+.*?src\\s*=\\s*([\"\\'])([^\\\\1]+?)\\\\1.*?\\>#i',\n\t\t\t\t'#<img[\\s/]+.*?src\\s*=\\s*?(([^\\s\"\\'=<>`]+)).*?\\>#i'\n\t\t\t),\n\t\t\t'\\\\2',\n\t\t\t$str\n\t\t);\n\t}\n\n\t// ----------------------------------------------------------------\n\n\t/**\n\t * URL-decode taking spaces into account\n\t *\n\t * @see\t\thttps://github.com/bcit-ci/CodeIgniter/issues/4877\n\t * @param\tarray\t$matches\n\t * @return\tstring\n\t */\n\tprotected function _urldecodespaces($matches)\n\t{\n\t\t$input    = $matches[0];\n\t\t$nospaces = preg_replace('#\\s+#', '', $input);\n\t\treturn ($nospaces === $input)\n\t\t\t? $input\n\t\t\t: rawurldecode($nospaces);\n\t}\n\n\t// ----------------------------------------------------------------\n\n\t/**\n\t * Compact Exploded Words\n\t *\n\t * Callback method for xss_clean() to remove whitespace from\n\t * things like 'j a v a s c r i p t'.\n\t *\n\t * @used-by\tCI_Security::xss_clean()\n\t * @param\tarray\t$matches\n\t * @return\tstring\n\t */\n\tprotected function _compact_exploded_words($matches)\n\t{\n\t\treturn preg_replace('/\\s+/s', '', $matches[1]).$matches[2];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Sanitize Naughty HTML\n\t *\n\t * Callback method for xss_clean() to remove naughty HTML elements.\n\t *\n\t * @used-by\tCI_Security::xss_clean()\n\t * @param\tarray\t$matches\n\t * @return\tstring\n\t */\n\tprotected function _sanitize_naughty_html($matches)\n\t{\n\t\tstatic $naughty_tags    = array(\n\t\t\t'alert', 'area', 'prompt', 'confirm', 'applet', 'audio', 'basefont', 'base', 'behavior', 'bgsound',\n\t\t\t'blink', 'body', 'embed', 'expression', 'form', 'frameset', 'frame', 'head', 'html', 'ilayer',\n\t\t\t'iframe', 'input', 'button', 'select', 'isindex', 'layer', 'link', 'meta', 'keygen', 'object',\n\t\t\t'plaintext', 'style', 'script', 'textarea', 'title', 'math', 'video', 'svg', 'xml', 'xss'\n\t\t);\n\n\t\tstatic $evil_attributes = array(\n\t\t\t'on\\w+', 'style', 'xmlns', 'formaction', 'form', 'xlink:href', 'FSCommand', 'seekSegmentTime'\n\t\t);\n\n\t\t// First, escape unclosed tags\n\t\tif (empty($matches['closeTag']))\n\t\t{\n\t\t\treturn '&lt;'.$matches[1];\n\t\t}\n\t\t// Is the element that we caught naughty? If so, escape it\n\t\telseif (in_array(strtolower($matches['tagName']), $naughty_tags, TRUE))\n\t\t{\n\t\t\treturn '&lt;'.$matches[1].'&gt;';\n\t\t}\n\t\t// For other tags, see if their attributes are \"evil\" and strip those\n\t\telseif (isset($matches['attributes']))\n\t\t{\n\t\t\t// We'll store the already filtered attributes here\n\t\t\t$attributes = array();\n\n\t\t\t// Attribute-catching pattern\n\t\t\t$attributes_pattern = '#'\n\t\t\t\t.'(?<name>[^\\s\\042\\047>/=]+)' // attribute characters\n\t\t\t\t// optional attribute-value\n\t\t\t\t.'(?:\\s*=(?<value>[^\\s\\042\\047=><`]+|\\s*\\042[^\\042]*\\042|\\s*\\047[^\\047]*\\047|\\s*(?U:[^\\s\\042\\047=><`]*)))' // attribute-value separator\n\t\t\t\t.'#i';\n\n\t\t\t// Blacklist pattern for evil attribute names\n\t\t\t$is_evil_pattern = '#^('.implode('|', $evil_attributes).')$#i';\n\n\t\t\t// Each iteration filters a single attribute\n\t\t\tdo\n\t\t\t{\n\t\t\t\t// Strip any non-alpha characters that may precede an attribute.\n\t\t\t\t// Browsers often parse these incorrectly and that has been a\n\t\t\t\t// of numerous XSS issues we've had.\n\t\t\t\t$matches['attributes'] = preg_replace('#^[^a-z]+#i', '', $matches['attributes']);\n\n\t\t\t\tif ( ! preg_match($attributes_pattern, $matches['attributes'], $attribute, PREG_OFFSET_CAPTURE))\n\t\t\t\t{\n\t\t\t\t\t// No (valid) attribute found? Discard everything else inside the tag\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\t// Is it indeed an \"evil\" attribute?\n\t\t\t\t\tpreg_match($is_evil_pattern, $attribute['name'][0])\n\t\t\t\t\t// Or does it have an equals sign, but no value and not quoted? Strip that too!\n\t\t\t\t\tOR (trim($attribute['value'][0]) === '')\n\t\t\t\t)\n\t\t\t\t{\n\t\t\t\t\t$attributes[] = 'xss=removed';\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$attributes[] = $attribute[0][0];\n\t\t\t\t}\n\n\t\t\t\t$matches['attributes'] = substr($matches['attributes'], $attribute[0][1] + strlen($attribute[0][0]));\n\t\t\t}\n\t\t\twhile ($matches['attributes'] !== '');\n\n\t\t\t$attributes = empty($attributes)\n\t\t\t\t? ''\n\t\t\t\t: ' '.implode(' ', $attributes);\n\t\t\treturn '<'.$matches['slash'].$matches['tagName'].$attributes.'>';\n\t\t}\n\n\t\treturn $matches[0];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * JS Link Removal\n\t *\n\t * Callback method for xss_clean() to sanitize links.\n\t *\n\t * This limits the PCRE backtracks, making it more performance friendly\n\t * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in\n\t * PHP 5.2+ on link-heavy strings.\n\t *\n\t * @used-by\tCI_Security::xss_clean()\n\t * @param\tarray\t$match\n\t * @return\tstring\n\t */\n\tprotected function _js_link_removal($match)\n\t{\n\t\treturn str_replace(\n\t\t\t$match[1],\n\t\t\tpreg_replace(\n\t\t\t\t'#href=.*?(?:(?:alert|prompt|confirm)(?:\\(|&\\#40;|`|&\\#96;)|javascript:|livescript:|mocha:|charset=|window\\.|\\(?document\\)?\\.|\\.cookie|<script|<xss|d\\s*a\\s*t\\s*a\\s*:)#si',\n\t\t\t\t'',\n\t\t\t\t$this->_filter_attributes($match[1])\n\t\t\t),\n\t\t\t$match[0]\n\t\t);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * JS Image Removal\n\t *\n\t * Callback method for xss_clean() to sanitize image tags.\n\t *\n\t * This limits the PCRE backtracks, making it more performance friendly\n\t * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in\n\t * PHP 5.2+ on image tag heavy strings.\n\t *\n\t * @used-by\tCI_Security::xss_clean()\n\t * @param\tarray\t$match\n\t * @return\tstring\n\t */\n\tprotected function _js_img_removal($match)\n\t{\n\t\treturn str_replace(\n\t\t\t$match[1],\n\t\t\tpreg_replace(\n\t\t\t\t'#src=.*?(?:(?:alert|prompt|confirm|eval)(?:\\(|&\\#40;|`|&\\#96;)|javascript:|livescript:|mocha:|charset=|window\\.|\\(?document\\)?\\.|\\.cookie|<script|<xss|base64\\s*,)#si',\n\t\t\t\t'',\n\t\t\t\t$this->_filter_attributes($match[1])\n\t\t\t),\n\t\t\t$match[0]\n\t\t);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Attribute Conversion\n\t *\n\t * @used-by\tCI_Security::xss_clean()\n\t * @param\tarray\t$match\n\t * @return\tstring\n\t */\n\tprotected function _convert_attribute($match)\n\t{\n\t\treturn str_replace(array('>', '<', '\\\\'), array('&gt;', '&lt;', '\\\\\\\\'), $match[0]);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Filter Attributes\n\t *\n\t * Filters tag attributes for consistency and safety.\n\t *\n\t * @used-by\tCI_Security::_js_img_removal()\n\t * @used-by\tCI_Security::_js_link_removal()\n\t * @param\tstring\t$str\n\t * @return\tstring\n\t */\n\tprotected function _filter_attributes($str)\n\t{\n\t\t$out = '';\n\t\tif (preg_match_all('#\\s*[a-z\\-]+\\s*=\\s*(\\042|\\047)([^\\\\1]*?)\\\\1#is', $str, $matches))\n\t\t{\n\t\t\tforeach ($matches[0] as $match)\n\t\t\t{\n\t\t\t\t$out .= preg_replace('#/\\*.*?\\*/#s', '', $match);\n\t\t\t}\n\t\t}\n\n\t\treturn $out;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * HTML Entity Decode Callback\n\t *\n\t * @used-by\tCI_Security::xss_clean()\n\t * @param\tarray\t$match\n\t * @return\tstring\n\t */\n\tprotected function _decode_entity($match)\n\t{\n\t\t// Protect GET variables in URLs\n\t\t// 901119URL5918AMP18930PROTECT8198\n\t\t$match = preg_replace('|\\&([a-z\\_0-9\\-]+)\\=([a-z\\_0-9\\-/]+)|i', $this->xss_hash().'\\\\1=\\\\2', $match[0]);\n\n\t\t// Decode, then un-protect URL GET vars\n\t\treturn str_replace(\n\t\t\t$this->xss_hash(),\n\t\t\t'&',\n\t\t\t$this->entity_decode($match, $this->charset)\n\t\t);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Do Never Allowed\n\t *\n\t * @used-by\tCI_Security::xss_clean()\n\t * @param \tstring\n\t * @return \tstring\n\t */\n\tprotected function _do_never_allowed($str)\n\t{\n\t\t$str = str_replace(array_keys($this->_never_allowed_str), $this->_never_allowed_str, $str);\n\n\t\tforeach ($this->_never_allowed_regex as $regex)\n\t\t{\n\t\t\t$str = preg_replace('#'.$regex.'#is', '[removed]', $str);\n\t\t}\n\n\t\treturn $str;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set CSRF Hash and Cookie\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _csrf_set_hash()\n\t{\n\t\tif ($this->_csrf_hash === NULL)\n\t\t{\n\t\t\t// If the cookie exists we will use its value.\n\t\t\t// We don't necessarily want to regenerate it with\n\t\t\t// each page load since a page could contain embedded\n\t\t\t// sub-pages causing this feature to fail\n\t\t\tif (isset($_COOKIE[$this->_csrf_cookie_name]) && is_string($_COOKIE[$this->_csrf_cookie_name])\n\t\t\t\t&& preg_match('#^[0-9a-f]{32}$#iS', $_COOKIE[$this->_csrf_cookie_name]) === 1)\n\t\t\t{\n\t\t\t\treturn $this->_csrf_hash = $_COOKIE[$this->_csrf_cookie_name];\n\t\t\t}\n\n\t\t\t$rand = $this->get_random_bytes(16);\n\t\t\t$this->_csrf_hash = ($rand === FALSE)\n\t\t\t\t? md5(uniqid(mt_rand(), TRUE))\n\t\t\t\t: bin2hex($rand);\n\t\t}\n\n\t\treturn $this->_csrf_hash;\n\t}\n}\n"
  },
  {
    "path": "system/core/URI.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * URI Class\n *\n * Parses URIs and determines routing\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tURI\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/uri.html\n */\nclass CI_URI {\n\n\t/**\n\t * List of cached URI segments\n\t *\n\t * @var\tarray\n\t */\n\tpublic $keyval = array();\n\n\t/**\n\t * Current URI string\n\t *\n\t * @var\tstring\n\t */\n\tpublic $uri_string = '';\n\n\t/**\n\t * List of URI segments\n\t *\n\t * Starts at 1 instead of 0.\n\t *\n\t * @var\tarray\n\t */\n\tpublic $segments = array();\n\n\t/**\n\t * List of routed URI segments\n\t *\n\t * Starts at 1 instead of 0.\n\t *\n\t * @var\tarray\n\t */\n\tpublic $rsegments = array();\n\n\t/**\n\t * Permitted URI chars\n\t *\n\t * PCRE character group allowed in URI segments\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_permitted_uri_chars;\n\n\t/**\n\t * Class constructor\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __construct(CI_Config $config)\n\t{\n\t\t$this->config = $config;\n\n\t\t// If it's a CLI request, ignore the configuration\n\t\tif (is_cli())\n\t\t{\n\t\t\t$this->_set_uri_string($this->_parse_argv(), TRUE);\n\t\t}\n\t\t// If query strings are enabled, we don't need to parse any segments.\n\t\telseif ($this->config->item('enable_query_strings') !== TRUE)\n\t\t{\n\t\t\t$this->_permitted_uri_chars = $this->config->item('permitted_uri_chars');\n\t\t\t$protocol = $this->config->item('uri_protocol');\n\t\t\tempty($protocol) && $protocol = 'REQUEST_URI';\n\n\t\t\tswitch ($protocol)\n\t\t\t{\n\t\t\t\tcase 'AUTO': // For BC purposes only\n\t\t\t\tcase 'REQUEST_URI':\n\t\t\t\t\t$uri = $this->_parse_request_uri();\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'QUERY_STRING':\n\t\t\t\t\t$uri = $this->_parse_query_string();\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'PATH_INFO':\n\t\t\t\tdefault:\n\t\t\t\t\t$uri = isset($_SERVER[$protocol])\n\t\t\t\t\t\t? $_SERVER[$protocol]\n\t\t\t\t\t\t: $this->_parse_request_uri();\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t$this->_set_uri_string($uri, FALSE);\n\t\t}\n\n\t\tlog_message('info', 'URI Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set URI String\n\t *\n\t * @param \tstring\t$str\tInput URI string\n\t * @param\tbool\t$is_cli\tWhether the input comes from CLI\n\t * @return\tvoid\n\t */\n\tprotected function _set_uri_string($str, $is_cli = FALSE)\n\t{\n\t\t// CLI requests have a bit simpler logic\n\t\tif ($is_cli)\n\t\t{\n\t\t\tif (($this->uri_string = trim($str, '/')) === '')\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t$this->segments[0] = NULL;\n\t\t\tforeach (explode('/', $this->uri_string) as $segment)\n\t\t\t{\n\t\t\t\tif (($segment = trim($segment)) !== '')\n\t\t\t\t{\n\t\t\t\t\t$this->segments[] = $segment;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tunset($this->segments[0]);\n\t\t\treturn;\n\t\t}\n\n\t\t// Filter out control characters and trim slashes\n\t\t$this->uri_string = trim(remove_invisible_characters($str, FALSE), '/');\n\n\t\tif ($this->uri_string === '')\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t// Remove the URL suffix, if present\n\t\tif (($suffix = (string) $this->config->item('url_suffix')) !== '')\n\t\t{\n\t\t\t$slen = strlen($suffix);\n\n\t\t\tif (substr($this->uri_string, -$slen) === $suffix)\n\t\t\t{\n\t\t\t\t$this->uri_string = substr($this->uri_string, 0, -$slen);\n\t\t\t}\n\t\t}\n\n\t\t$this->segments[0] = NULL;\n\t\tforeach (explode('/', trim($this->uri_string, '/')) as $segment)\n\t\t{\n\t\t\t$segment = trim($segment);\n\t\t\t// Filter segments for security\n\t\t\t$this->filter_uri($segment);\n\n\t\t\tif ($segment !== '')\n\t\t\t{\n\t\t\t\t$this->segments[] = $segment;\n\t\t\t}\n\t\t}\n\n\t\tunset($this->segments[0]);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Parse REQUEST_URI\n\t *\n\t * Will parse REQUEST_URI and automatically detect the URI from it,\n\t * while fixing the query string if necessary.\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _parse_request_uri()\n\t{\n\t\tif ( ! isset($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME']))\n\t\t{\n\t\t\treturn '';\n\t\t}\n\n\t\t// parse_url() returns false if no host is present, but the path or query string\n\t\t// contains a colon followed by a number\n\t\t$uri = parse_url('http://dummy'.$_SERVER['REQUEST_URI']);\n\t\t$query = isset($uri['query']) ? $uri['query'] : '';\n\t\t$uri = isset($uri['path']) ? $uri['path'] : '';\n\n\t\tif (isset($_SERVER['SCRIPT_NAME'][0]))\n\t\t{\n\t\t\tif (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0)\n\t\t\t{\n\t\t\t\t$uri = (string) substr($uri, strlen($_SERVER['SCRIPT_NAME']));\n\t\t\t}\n\t\t\telseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0)\n\t\t\t{\n\t\t\t\t$uri = (string) substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME'])));\n\t\t\t}\n\t\t}\n\n\t\t// This section ensures that even on servers that require the URI to be in the query string (Nginx) a correct\n\t\t// URI is found, and also fixes the QUERY_STRING server var and $_GET array.\n\t\tif (trim($uri, '/') === '' && strncmp($query, '/', 1) === 0)\n\t\t{\n\t\t\t$query = explode('?', $query, 2);\n\t\t\t$uri = $query[0];\n\t\t\t$_SERVER['QUERY_STRING'] = isset($query[1]) ? $query[1] : '';\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$_SERVER['QUERY_STRING'] = $query;\n\t\t}\n\n\t\tparse_str($_SERVER['QUERY_STRING'], $_GET);\n\n\t\tif ($uri === '/' OR $uri === '')\n\t\t{\n\t\t\treturn '/';\n\t\t}\n\n\t\t// Do some final cleaning of the URI and return it\n\t\treturn $this->_remove_relative_directory($uri);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Parse QUERY_STRING\n\t *\n\t * Will parse QUERY_STRING and automatically detect the URI from it.\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _parse_query_string()\n\t{\n\t\t$uri = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING');\n\n\t\tif (trim($uri, '/') === '')\n\t\t{\n\t\t\treturn '';\n\t\t}\n\t\telseif (strncmp($uri, '/', 1) === 0)\n\t\t{\n\t\t\t$uri = explode('?', $uri, 2);\n\t\t\t$_SERVER['QUERY_STRING'] = isset($uri[1]) ? $uri[1] : '';\n\t\t\t$uri = $uri[0];\n\t\t}\n\n\t\tparse_str($_SERVER['QUERY_STRING'], $_GET);\n\n\t\treturn $this->_remove_relative_directory($uri);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Parse CLI arguments\n\t *\n\t * Take each command line argument and assume it is a URI segment.\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _parse_argv()\n\t{\n\t\t$args = array_slice($_SERVER['argv'], 1);\n\t\treturn $args ? implode('/', $args) : '';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Remove relative directory (../) and multi slashes (///)\n\t *\n\t * Do some final cleaning of the URI and return it, currently only used in self::_parse_request_uri()\n\t *\n\t * @param\tstring\t$uri\n\t * @return\tstring\n\t */\n\tprotected function _remove_relative_directory($uri)\n\t{\n\t\t$uris = array();\n\t\t$tok = strtok($uri, '/');\n\t\twhile ($tok !== FALSE)\n\t\t{\n\t\t\tif (( ! empty($tok) OR $tok === '0') && $tok !== '..')\n\t\t\t{\n\t\t\t\t$uris[] = $tok;\n\t\t\t}\n\t\t\t$tok = strtok('/');\n\t\t}\n\n\t\treturn implode('/', $uris);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Filter URI\n\t *\n\t * Filters segments for malicious characters.\n\t *\n\t * @param\tstring\t$str\n\t * @return\tvoid\n\t */\n\tpublic function filter_uri(&$str)\n\t{\n\t\tif ( ! empty($str) && ! empty($this->_permitted_uri_chars) && ! preg_match('/^['.$this->_permitted_uri_chars.']+$/i'.(UTF8_ENABLED ? 'u' : ''), $str))\n\t\t{\n\t\t\tshow_error('The URI you submitted has disallowed characters.', 400);\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch URI Segment\n\t *\n\t * @see\t\tCI_URI::$segments\n\t * @param\tint\t\t$n\t\tIndex\n\t * @param\tmixed\t\t$no_result\tWhat to return if the segment index is not found\n\t * @return\tmixed\n\t */\n\tpublic function segment($n, $no_result = NULL)\n\t{\n\t\treturn isset($this->segments[$n]) ? $this->segments[$n] : $no_result;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch URI \"routed\" Segment\n\t *\n\t * Returns the re-routed URI segment (assuming routing rules are used)\n\t * based on the index provided. If there is no routing, will return\n\t * the same result as CI_URI::segment().\n\t *\n\t * @see\t\tCI_URI::$rsegments\n\t * @see\t\tCI_URI::segment()\n\t * @param\tint\t\t$n\t\tIndex\n\t * @param\tmixed\t\t$no_result\tWhat to return if the segment index is not found\n\t * @return\tmixed\n\t */\n\tpublic function rsegment($n, $no_result = NULL)\n\t{\n\t\treturn isset($this->rsegments[$n]) ? $this->rsegments[$n] : $no_result;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * URI to assoc\n\t *\n\t * Generates an associative array of URI data starting at the supplied\n\t * segment index. For example, if this is your URI:\n\t *\n\t *\texample.com/user/search/name/joe/location/UK/gender/male\n\t *\n\t * You can use this method to generate an array with this prototype:\n\t *\n\t *\tarray (\n\t *\t\tname => joe\n\t *\t\tlocation => UK\n\t *\t\tgender => male\n\t *\t )\n\t *\n\t * @param\tint\t$n\t\tIndex (default: 3)\n\t * @param\tarray\t$default\tDefault values\n\t * @return\tarray\n\t */\n\tpublic function uri_to_assoc($n = 3, $default = array())\n\t{\n\t\treturn $this->_uri_to_assoc($n, $default, 'segment');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Routed URI to assoc\n\t *\n\t * Identical to CI_URI::uri_to_assoc(), only it uses the re-routed\n\t * segment array.\n\t *\n\t * @see\t\tCI_URI::uri_to_assoc()\n\t * @param \tint\t$n\t\tIndex (default: 3)\n\t * @param \tarray\t$default\tDefault values\n\t * @return \tarray\n\t */\n\tpublic function ruri_to_assoc($n = 3, $default = array())\n\t{\n\t\treturn $this->_uri_to_assoc($n, $default, 'rsegment');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Internal URI-to-assoc\n\t *\n\t * Generates a key/value pair from the URI string or re-routed URI string.\n\t *\n\t * @used-by\tCI_URI::uri_to_assoc()\n\t * @used-by\tCI_URI::ruri_to_assoc()\n\t * @param\tint\t$n\t\tIndex (default: 3)\n\t * @param\tarray\t$default\tDefault values\n\t * @param\tstring\t$which\t\tArray name ('segment' or 'rsegment')\n\t * @return\tarray\n\t */\n\tprotected function _uri_to_assoc($n = 3, $default = array(), $which = 'segment')\n\t{\n\t\tif ( ! is_numeric($n))\n\t\t{\n\t\t\treturn $default;\n\t\t}\n\n\t\tif (isset($this->keyval[$which], $this->keyval[$which][$n]))\n\t\t{\n\t\t\treturn $this->keyval[$which][$n];\n\t\t}\n\n\t\t$total_segments = \"total_{$which}s\";\n\t\t$segment_array = \"{$which}_array\";\n\n\t\tif ($this->$total_segments() < $n)\n\t\t{\n\t\t\treturn (count($default) === 0)\n\t\t\t\t? array()\n\t\t\t\t: array_fill_keys($default, NULL);\n\t\t}\n\n\t\t$segments = array_slice($this->$segment_array(), ($n - 1));\n\t\t$i = 0;\n\t\t$lastval = '';\n\t\t$retval = array();\n\t\tforeach ($segments as $seg)\n\t\t{\n\t\t\tif ($i % 2)\n\t\t\t{\n\t\t\t\t$retval[$lastval] = $seg;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$retval[$seg] = NULL;\n\t\t\t\t$lastval = $seg;\n\t\t\t}\n\n\t\t\t$i++;\n\t\t}\n\n\t\tif (count($default) > 0)\n\t\t{\n\t\t\tforeach ($default as $val)\n\t\t\t{\n\t\t\t\tif ( ! array_key_exists($val, $retval))\n\t\t\t\t{\n\t\t\t\t\t$retval[$val] = NULL;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Cache the array for reuse\n\t\tisset($this->keyval[$which]) OR $this->keyval[$which] = array();\n\t\t$this->keyval[$which][$n] = $retval;\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Assoc to URI\n\t *\n\t * Generates a URI string from an associative array.\n\t *\n\t * @param\tarray\t$array\tInput array of key/value pairs\n\t * @return\tstring\tURI string\n\t */\n\tpublic function assoc_to_uri($array)\n\t{\n\t\t$temp = array();\n\t\tforeach ((array) $array as $key => $val)\n\t\t{\n\t\t\t$temp[] = $key;\n\t\t\t$temp[] = $val;\n\t\t}\n\n\t\treturn implode('/', $temp);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Slash segment\n\t *\n\t * Fetches an URI segment with a slash.\n\t *\n\t * @param\tint\t$n\tIndex\n\t * @param\tstring\t$where\tWhere to add the slash ('trailing' or 'leading')\n\t * @return\tstring\n\t */\n\tpublic function slash_segment($n, $where = 'trailing')\n\t{\n\t\treturn $this->_slash_segment($n, $where, 'segment');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Slash routed segment\n\t *\n\t * Fetches an URI routed segment with a slash.\n\t *\n\t * @param\tint\t$n\tIndex\n\t * @param\tstring\t$where\tWhere to add the slash ('trailing' or 'leading')\n\t * @return\tstring\n\t */\n\tpublic function slash_rsegment($n, $where = 'trailing')\n\t{\n\t\treturn $this->_slash_segment($n, $where, 'rsegment');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Internal Slash segment\n\t *\n\t * Fetches an URI Segment and adds a slash to it.\n\t *\n\t * @used-by\tCI_URI::slash_segment()\n\t * @used-by\tCI_URI::slash_rsegment()\n\t *\n\t * @param\tint\t$n\tIndex\n\t * @param\tstring\t$where\tWhere to add the slash ('trailing' or 'leading')\n\t * @param\tstring\t$which\tArray name ('segment' or 'rsegment')\n\t * @return\tstring\n\t */\n\tprotected function _slash_segment($n, $where = 'trailing', $which = 'segment')\n\t{\n\t\t$leading = $trailing = '/';\n\n\t\tif ($where === 'trailing')\n\t\t{\n\t\t\t$leading\t= '';\n\t\t}\n\t\telseif ($where === 'leading')\n\t\t{\n\t\t\t$trailing\t= '';\n\t\t}\n\n\t\treturn $leading.$this->$which($n).$trailing;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Segment Array\n\t *\n\t * @return\tarray\tCI_URI::$segments\n\t */\n\tpublic function segment_array()\n\t{\n\t\treturn $this->segments;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Routed Segment Array\n\t *\n\t * @return\tarray\tCI_URI::$rsegments\n\t */\n\tpublic function rsegment_array()\n\t{\n\t\treturn $this->rsegments;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Total number of segments\n\t *\n\t * @return\tint\n\t */\n\tpublic function total_segments()\n\t{\n\t\treturn count($this->segments);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Total number of routed segments\n\t *\n\t * @return\tint\n\t */\n\tpublic function total_rsegments()\n\t{\n\t\treturn count($this->rsegments);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch URI string\n\t *\n\t * @return\tstring\tCI_URI::$uri_string\n\t */\n\tpublic function uri_string()\n\t{\n\t\treturn $this->uri_string;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch Re-routed URI string\n\t *\n\t * @return\tstring\n\t */\n\tpublic function ruri_string()\n\t{\n\t\treturn ltrim(load_class('Router', 'core')->directory, '/').implode('/', $this->rsegments);\n\t}\n\n}\n"
  },
  {
    "path": "system/core/Utf8.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 2.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Utf8 Class\n *\n * Provides support for UTF-8 environments\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tUTF-8\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/utf8.html\n */\nclass CI_Utf8 {\n\n\t/**\n\t * Class constructor\n\t *\n\t * Determines if UTF-8 support is to be enabled.\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __construct($charset)\n\t{\n\t\tif (\n\t\t\tdefined('PREG_BAD_UTF8_ERROR')                     // PCRE must support UTF-8\n\t\t\t&& (ICONV_ENABLED === TRUE OR MB_ENABLED === TRUE) // iconv or mbstring must be installed\n\t\t\t&& $charset === 'UTF-8'                            // Application charset must be UTF-8\n\t\t)\n\t\t{\n\t\t\tdefine('UTF8_ENABLED', TRUE);\n\t\t\tlog_message('info', 'UTF-8 Support Enabled');\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdefine('UTF8_ENABLED', FALSE);\n\t\t\tlog_message('info', 'UTF-8 Support Disabled');\n\t\t}\n\n\t\tlog_message('info', 'Utf8 Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Clean UTF-8 strings\n\t *\n\t * Ensures strings contain only valid UTF-8 characters.\n\t *\n\t * @param\tstring\t$str\tString to clean\n\t * @return\tstring\n\t */\n\tpublic function clean_string($str)\n\t{\n\t\tif ($this->is_ascii($str) === FALSE)\n\t\t{\n\t\t\tif (MB_ENABLED)\n\t\t\t{\n\t\t\t\t$str = mb_convert_encoding($str, 'UTF-8', 'UTF-8');\n\t\t\t}\n\t\t\telseif (ICONV_ENABLED)\n\t\t\t{\n\t\t\t\t$str = @iconv('UTF-8', 'UTF-8//IGNORE', $str);\n\t\t\t}\n\t\t}\n\n\t\treturn $str;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Remove ASCII control characters\n\t *\n\t * Removes all ASCII control characters except horizontal tabs,\n\t * line feeds, and carriage returns, as all others can cause\n\t * problems in XML.\n\t *\n\t * @param\tstring\t$str\tString to clean\n\t * @return\tstring\n\t */\n\tpublic function safe_ascii_for_xml($str)\n\t{\n\t\treturn remove_invisible_characters($str, FALSE);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Convert to UTF-8\n\t *\n\t * Attempts to convert a string to UTF-8.\n\t *\n\t * @param\tstring\t$str\t\tInput string\n\t * @param\tstring\t$encoding\tInput encoding\n\t * @return\tstring\t$str encoded in UTF-8 or FALSE on failure\n\t */\n\tpublic function convert_to_utf8($str, $encoding)\n\t{\n\t\tif (MB_ENABLED)\n\t\t{\n\t\t\treturn mb_convert_encoding($str, 'UTF-8', $encoding);\n\t\t}\n\t\telseif (ICONV_ENABLED)\n\t\t{\n\t\t\treturn @iconv($encoding, 'UTF-8', $str);\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Is ASCII?\n\t *\n\t * Tests if a string is standard 7-bit ASCII or not.\n\t *\n\t * @param\tstring\t$str\tString to check\n\t * @return\tbool\n\t */\n\tpublic function is_ascii($str)\n\t{\n\t\treturn (preg_match('/[^\\x00-\\x7F]/S', $str) === 0);\n\t}\n\n}\n"
  },
  {
    "path": "system/core/compat/hash.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PHP ext/hash compatibility package\n *\n * @package\t\tCodeIgniter\n * @subpackage\tCodeIgniter\n * @category\tCompatibility\n * @author\t\tAndrey Andreev\n * @link\t\thttps://codeigniter.com/userguide3/\n * @link\t\thttps://secure.php.net/hash\n */\n\n// ------------------------------------------------------------------------\n\nif (is_php('5.6'))\n{\n\treturn;\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('hash_equals'))\n{\n\t/**\n\t * hash_equals()\n\t *\n\t * @link\thttps://secure.php.net/hash_equals\n\t * @param\tstring\t$known_string\n\t * @param\tstring\t$user_string\n\t * @return\tbool\n\t */\n\tfunction hash_equals($known_string, $user_string)\n\t{\n\t\tif ( ! is_string($known_string))\n\t\t{\n\t\t\ttrigger_error('hash_equals(): Expected known_string to be a string, '.strtolower(gettype($known_string)).' given', E_USER_WARNING);\n\t\t\treturn FALSE;\n\t\t}\n\t\telseif ( ! is_string($user_string))\n\t\t{\n\t\t\ttrigger_error('hash_equals(): Expected user_string to be a string, '.strtolower(gettype($user_string)).' given', E_USER_WARNING);\n\t\t\treturn FALSE;\n\t\t}\n\t\telseif (($length = strlen($known_string)) !== strlen($user_string))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$diff = 0;\n\t\tfor ($i = 0; $i < $length; $i++)\n\t\t{\n\t\t\t$diff |= ord($known_string[$i]) ^ ord($user_string[$i]);\n\t\t}\n\n\t\treturn ($diff === 0);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif (is_php('5.5'))\n{\n\treturn;\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('hash_pbkdf2'))\n{\n\t/**\n\t * hash_pbkdf2()\n\t *\n\t * @link\thttps://secure.php.net/hash_pbkdf2\n\t * @param\tstring\t$algo\n\t * @param\tstring\t$password\n\t * @param\tstring\t$salt\n\t * @param\tint\t$iterations\n\t * @param\tint\t$length\n\t * @param\tbool\t$raw_output\n\t * @return\tstring\n\t */\n\tfunction hash_pbkdf2($algo, $password, $salt, $iterations, $length = 0, $raw_output = FALSE)\n\t{\n\t\tif ( ! in_array(strtolower($algo), hash_algos(), TRUE))\n\t\t{\n\t\t\ttrigger_error('hash_pbkdf2(): Unknown hashing algorithm: '.$algo, E_USER_WARNING);\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif (($type = gettype($iterations)) !== 'integer')\n\t\t{\n\t\t\tif ($type === 'object' && method_exists($iterations, '__toString'))\n\t\t\t{\n\t\t\t\t$iterations = (string) $iterations;\n\t\t\t}\n\n\t\t\tif (is_string($iterations) && is_numeric($iterations))\n\t\t\t{\n\t\t\t\t$iterations = (int) $iterations;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttrigger_error('hash_pbkdf2() expects parameter 4 to be long, '.$type.' given', E_USER_WARNING);\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t}\n\n\t\tif ($iterations < 1)\n\t\t{\n\t\t\ttrigger_error('hash_pbkdf2(): Iterations must be a positive integer: '.$iterations, E_USER_WARNING);\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif (($type = gettype($length)) !== 'integer')\n\t\t{\n\t\t\tif ($type === 'object' && method_exists($length, '__toString'))\n\t\t\t{\n\t\t\t\t$length = (string) $length;\n\t\t\t}\n\n\t\t\tif (is_string($length) && is_numeric($length))\n\t\t\t{\n\t\t\t\t$length = (int) $length;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttrigger_error('hash_pbkdf2() expects parameter 5 to be long, '.$type.' given', E_USER_WARNING);\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t}\n\n\t\tif ($length < 0)\n\t\t{\n\t\t\ttrigger_error('hash_pbkdf2(): Length must be greater than or equal to 0: '.$length, E_USER_WARNING);\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$hash_length = defined('MB_OVERLOAD_STRING')\n\t\t\t? mb_strlen(hash($algo, NULL, TRUE), '8bit')\n\t\t\t: strlen(hash($algo, NULL, TRUE));\n\t\tempty($length) && $length = $hash_length;\n\n\t\t// Pre-hash password inputs longer than the algorithm's block size\n\t\t// (i.e. prepare HMAC key) to mitigate potential DoS attacks.\n\t\tstatic $block_sizes;\n\t\tempty($block_sizes) && $block_sizes = array(\n\t\t\t'gost' => 32,\n\t\t\t'haval128,3' => 128,\n\t\t\t'haval160,3' => 128,\n\t\t\t'haval192,3' => 128,\n\t\t\t'haval224,3' => 128,\n\t\t\t'haval256,3' => 128,\n\t\t\t'haval128,4' => 128,\n\t\t\t'haval160,4' => 128,\n\t\t\t'haval192,4' => 128,\n\t\t\t'haval224,4' => 128,\n\t\t\t'haval256,4' => 128,\n\t\t\t'haval128,5' => 128,\n\t\t\t'haval160,5' => 128,\n\t\t\t'haval192,5' => 128,\n\t\t\t'haval224,5' => 128,\n\t\t\t'haval256,5' => 128,\n\t\t\t'md2' => 16,\n\t\t\t'md4' => 64,\n\t\t\t'md5' => 64,\n\t\t\t'ripemd128' => 64,\n\t\t\t'ripemd160' => 64,\n\t\t\t'ripemd256' => 64,\n\t\t\t'ripemd320' => 64,\n\t\t\t'sha1' => 64,\n\t\t\t'sha224' => 64,\n\t\t\t'sha256' => 64,\n\t\t\t'sha384' => 128,\n\t\t\t'sha512' => 128,\n\t\t\t'snefru' => 32,\n\t\t\t'snefru256' => 32,\n\t\t\t'tiger128,3' => 64,\n\t\t\t'tiger160,3' => 64,\n\t\t\t'tiger192,3' => 64,\n\t\t\t'tiger128,4' => 64,\n\t\t\t'tiger160,4' => 64,\n\t\t\t'tiger192,4' => 64,\n\t\t\t'whirlpool' => 64\n\t\t);\n\n\t\tif (isset($block_sizes[$algo], $password[$block_sizes[$algo]]))\n\t\t{\n\t\t\t$password = hash($algo, $password, TRUE);\n\t\t}\n\n\t\t$hash = '';\n\t\t// Note: Blocks are NOT 0-indexed\n\t\tfor ($bc = (int) ceil($length / $hash_length), $bi = 1; $bi <= $bc; $bi++)\n\t\t{\n\t\t\t$key = $derived_key = hash_hmac($algo, $salt.pack('N', $bi), $password, TRUE);\n\t\t\tfor ($i = 1; $i < $iterations; $i++)\n\t\t\t{\n\t\t\t\t$derived_key ^= $key = hash_hmac($algo, $key, $password, TRUE);\n\t\t\t}\n\n\t\t\t$hash .= $derived_key;\n\t\t}\n\n\t\t// This is not RFC-compatible, but we're aiming for natural PHP compatibility\n\t\tif ( ! $raw_output)\n\t\t{\n\t\t\t$hash = bin2hex($hash);\n\t\t}\n\n\t\treturn defined('MB_OVERLOAD_STRING')\n\t\t\t? mb_substr($hash, 0, $length, '8bit')\n\t\t\t: substr($hash, 0, $length);\n\t}\n}\n"
  },
  {
    "path": "system/core/compat/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/core/compat/mbstring.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PHP ext/mbstring compatibility package\n *\n * @package\t\tCodeIgniter\n * @subpackage\tCodeIgniter\n * @category\tCompatibility\n * @author\t\tAndrey Andreev\n * @link\t\thttps://codeigniter.com/userguide3/\n * @link\t\thttps://secure.php.net/mbstring\n */\n\n// ------------------------------------------------------------------------\n\nif (MB_ENABLED === TRUE)\n{\n\treturn;\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('mb_strlen'))\n{\n\t/**\n\t * mb_strlen()\n\t *\n\t * WARNING: This function WILL fall-back to strlen()\n\t * if iconv is not available!\n\t *\n\t * @link\thttps://secure.php.net/mb_strlen\n\t * @param\tstring\t$str\n\t * @param\tstring\t$encoding\n\t * @return\tint\n\t */\n\tfunction mb_strlen($str, $encoding = NULL)\n\t{\n\t\tif (ICONV_ENABLED === TRUE)\n\t\t{\n\t\t\treturn iconv_strlen($str, isset($encoding) ? $encoding : config_item('charset'));\n\t\t}\n\n\t\tlog_message('debug', 'Compatibility (mbstring): iconv_strlen() is not available, falling back to strlen().');\n\t\treturn strlen($str);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('mb_strpos'))\n{\n\t/**\n\t * mb_strpos()\n\t *\n\t * WARNING: This function WILL fall-back to strpos()\n\t * if iconv is not available!\n\t *\n\t * @link\thttps://secure.php.net/mb_strpos\n\t * @param\tstring\t$haystack\n\t * @param\tstring\t$needle\n\t * @param\tint\t$offset\n\t * @param\tstring\t$encoding\n\t * @return\tmixed\n\t */\n\tfunction mb_strpos($haystack, $needle, $offset = 0, $encoding = NULL)\n\t{\n\t\tif (ICONV_ENABLED === TRUE)\n\t\t{\n\t\t\treturn iconv_strpos($haystack, $needle, $offset, isset($encoding) ? $encoding : config_item('charset'));\n\t\t}\n\n\t\tlog_message('debug', 'Compatibility (mbstring): iconv_strpos() is not available, falling back to strpos().');\n\t\treturn strpos($haystack, $needle, $offset);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('mb_substr'))\n{\n\t/**\n\t * mb_substr()\n\t *\n\t * WARNING: This function WILL fall-back to substr()\n\t * if iconv is not available.\n\t *\n\t * @link\thttps://secure.php.net/mb_substr\n\t * @param\tstring\t$str\n\t * @param\tint\t$start\n\t * @param\tint \t$length\n\t * @param\tstring\t$encoding\n\t * @return\tstring\n\t */\n\tfunction mb_substr($str, $start, $length = NULL, $encoding = NULL)\n\t{\n\t\tif (ICONV_ENABLED === TRUE)\n\t\t{\n\t\t\tisset($encoding) OR $encoding = config_item('charset');\n\t\t\treturn iconv_substr(\n\t\t\t\t$str,\n\t\t\t\t$start,\n\t\t\t\tisset($length) ? $length : iconv_strlen($str, $encoding), // NULL doesn't work\n\t\t\t\t$encoding\n\t\t\t);\n\t\t}\n\n\t\tlog_message('debug', 'Compatibility (mbstring): iconv_substr() is not available, falling back to substr().');\n\t\treturn isset($length)\n\t\t\t? substr($str, $start, $length)\n\t\t\t: substr($str, $start);\n\t}\n}\n"
  },
  {
    "path": "system/core/compat/password.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PHP ext/standard/password compatibility package\n *\n * @package\t\tCodeIgniter\n * @subpackage\tCodeIgniter\n * @category\tCompatibility\n * @author\t\tAndrey Andreev\n * @link\t\thttps://codeigniter.com/userguide3/\n * @link\t\thttps://secure.php.net/password\n */\n\n// ------------------------------------------------------------------------\n\nif (is_php('5.5') OR ! defined('CRYPT_BLOWFISH') OR CRYPT_BLOWFISH !== 1 OR defined('HHVM_VERSION'))\n{\n\treturn;\n}\n\n// ------------------------------------------------------------------------\n\ndefined('PASSWORD_BCRYPT') OR define('PASSWORD_BCRYPT', 1);\ndefined('PASSWORD_DEFAULT') OR define('PASSWORD_DEFAULT', PASSWORD_BCRYPT);\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('password_get_info'))\n{\n\t/**\n\t * password_get_info()\n\t *\n\t * @link\thttps://secure.php.net/password_get_info\n\t * @param\tstring\t$hash\n\t * @return\tarray\n\t */\n\tfunction password_get_info($hash)\n\t{\n\t\treturn (strlen($hash) < 60 OR sscanf($hash, '$2y$%d', $hash) !== 1)\n\t\t\t? array('algo' => 0, 'algoName' => 'unknown', 'options' => array())\n\t\t\t: array('algo' => 1, 'algoName' => 'bcrypt', 'options' => array('cost' => $hash));\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('password_hash'))\n{\n\t/**\n\t * password_hash()\n\t *\n\t * @link\thttps://secure.php.net/password_hash\n\t * @param\tstring\t$password\n\t * @param\tint\t$algo\n\t * @param\tarray\t$options\n\t * @return\tmixed\n\t */\n\tfunction password_hash($password, $algo, array $options = array())\n\t{\n\t\tstatic $func_overload;\n\t\tisset($func_overload) OR $func_overload = (extension_loaded('mbstring') && ini_get('mbstring.func_overload'));\n\n\t\tif ($algo !== 1)\n\t\t{\n\t\t\ttrigger_error('password_hash(): Unknown hashing algorithm: '.(int) $algo, E_USER_WARNING);\n\t\t\treturn NULL;\n\t\t}\n\n\t\tif (isset($options['cost']) && ($options['cost'] < 4 OR $options['cost'] > 31))\n\t\t{\n\t\t\ttrigger_error('password_hash(): Invalid bcrypt cost parameter specified: '.(int) $options['cost'], E_USER_WARNING);\n\t\t\treturn NULL;\n\t\t}\n\n\t\tif (isset($options['salt']) && ($saltlen = ($func_overload ? mb_strlen($options['salt'], '8bit') : strlen($options['salt']))) < 22)\n\t\t{\n\t\t\ttrigger_error('password_hash(): Provided salt is too short: '.$saltlen.' expecting 22', E_USER_WARNING);\n\t\t\treturn NULL;\n\t\t}\n\t\telseif ( ! isset($options['salt']))\n\t\t{\n\t\t\tif (function_exists('random_bytes'))\n\t\t\t{\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\t$options['salt'] = random_bytes(16);\n\t\t\t\t}\n\t\t\t\tcatch (Exception $e)\n\t\t\t\t{\n\t\t\t\t\tlog_message('error', 'compat/password: Error while trying to use random_bytes(): '.$e->getMessage());\n\t\t\t\t\treturn FALSE;\n\t\t\t\t}\n\t\t\t}\n\t\t\telseif (defined('MCRYPT_DEV_URANDOM'))\n\t\t\t{\n\t\t\t\t$options['salt'] = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);\n\t\t\t}\n\t\t\telseif (DIRECTORY_SEPARATOR === '/' && (is_readable($dev = '/dev/arandom') OR is_readable($dev = '/dev/urandom')))\n\t\t\t{\n\t\t\t\tif (($fp = fopen($dev, 'rb')) === FALSE)\n\t\t\t\t{\n\t\t\t\t\tlog_message('error', 'compat/password: Unable to open '.$dev.' for reading.');\n\t\t\t\t\treturn FALSE;\n\t\t\t\t}\n\n\t\t\t\t// Try not to waste entropy ...\n\t\t\t\tstream_set_chunk_size($fp, 16);\n\n\t\t\t\t$options['salt'] = '';\n\t\t\t\tfor ($read = 0; $read < 16; $read = ($func_overload) ? mb_strlen($options['salt'], '8bit') : strlen($options['salt']))\n\t\t\t\t{\n\t\t\t\t\tif (($read = fread($fp, 16 - $read)) === FALSE)\n\t\t\t\t\t{\n\t\t\t\t\t\tlog_message('error', 'compat/password: Error while reading from '.$dev.'.');\n\t\t\t\t\t\treturn FALSE;\n\t\t\t\t\t}\n\t\t\t\t\t$options['salt'] .= $read;\n\t\t\t\t}\n\n\t\t\t\tfclose($fp);\n\t\t\t}\n\t\t\telseif (function_exists('openssl_random_pseudo_bytes'))\n\t\t\t{\n\t\t\t\t$is_secure = NULL;\n\t\t\t\t$options['salt'] = openssl_random_pseudo_bytes(16, $is_secure);\n\t\t\t\tif ($is_secure !== TRUE)\n\t\t\t\t{\n\t\t\t\t\tlog_message('error', 'compat/password: openssl_random_pseudo_bytes() set the $cryto_strong flag to FALSE');\n\t\t\t\t\treturn FALSE;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tlog_message('error', 'compat/password: No CSPRNG available.');\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\t$options['salt'] = str_replace('+', '.', rtrim(base64_encode($options['salt']), '='));\n\t\t}\n\t\telseif ( ! preg_match('#^[a-zA-Z0-9./]+$#D', $options['salt']))\n\t\t{\n\t\t\t$options['salt'] = str_replace('+', '.', rtrim(base64_encode($options['salt']), '='));\n\t\t}\n\n\t\tisset($options['cost']) OR $options['cost'] = 10;\n\n\t\treturn (strlen($password = crypt($password, sprintf('$2y$%02d$%s', $options['cost'], $options['salt']))) === 60)\n\t\t\t? $password\n\t\t\t: FALSE;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('password_needs_rehash'))\n{\n\t/**\n\t * password_needs_rehash()\n\t *\n\t * @link\thttps://secure.php.net/password_needs_rehash\n\t * @param\tstring\t$hash\n\t * @param\tint\t$algo\n\t * @param\tarray\t$options\n\t * @return\tbool\n\t */\n\tfunction password_needs_rehash($hash, $algo, array $options = array())\n\t{\n\t\t$info = password_get_info($hash);\n\n\t\tif ($algo !== $info['algo'])\n\t\t{\n\t\t\treturn TRUE;\n\t\t}\n\t\telseif ($algo === 1)\n\t\t{\n\t\t\t$options['cost'] = isset($options['cost']) ? (int) $options['cost'] : 10;\n\t\t\treturn ($info['options']['cost'] !== $options['cost']);\n\t\t}\n\n\t\t// Odd at first glance, but according to a comment in PHP's own unit tests,\n\t\t// because it is an unknown algorithm - it's valid and therefore doesn't\n\t\t// need rehashing.\n\t\treturn FALSE;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('password_verify'))\n{\n\t/**\n\t * password_verify()\n\t *\n\t * @link\thttps://secure.php.net/password_verify\n\t * @param\tstring\t$password\n\t * @param\tstring\t$hash\n\t * @return\tbool\n\t */\n\tfunction password_verify($password, $hash)\n\t{\n\t\tif (strlen($hash) !== 60 OR strlen($password = crypt($password, $hash)) !== 60)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$compare = 0;\n\t\tfor ($i = 0; $i < 60; $i++)\n\t\t{\n\t\t\t$compare |= (ord($password[$i]) ^ ord($hash[$i]));\n\t\t}\n\n\t\treturn ($compare === 0);\n\t}\n}\n"
  },
  {
    "path": "system/core/compat/standard.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PHP ext/standard compatibility package\n *\n * @package\t\tCodeIgniter\n * @subpackage\tCodeIgniter\n * @category\tCompatibility\n * @author\t\tAndrey Andreev\n * @link\t\thttps://codeigniter.com/userguide3/\n */\n\n// ------------------------------------------------------------------------\n\nif (is_php('5.5'))\n{\n\treturn;\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('array_column'))\n{\n\t/**\n\t * array_column()\n\t *\n\t * @link\thttps://secure.php.net/array_column\n\t * @param\tarray\t$array\n\t * @param\tmixed\t$column_key\n\t * @param\tmixed\t$index_key\n\t * @return\tarray\n\t */\n\tfunction array_column(array $array, $column_key, $index_key = NULL)\n\t{\n\t\tif ( ! in_array($type = gettype($column_key), array('integer', 'string', 'NULL'), TRUE))\n\t\t{\n\t\t\tif ($type === 'double')\n\t\t\t{\n\t\t\t\t$column_key = (int) $column_key;\n\t\t\t}\n\t\t\telseif ($type === 'object' && method_exists($column_key, '__toString'))\n\t\t\t{\n\t\t\t\t$column_key = (string) $column_key;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttrigger_error('array_column(): The column key should be either a string or an integer', E_USER_WARNING);\n\t\t\t\treturn FALSE;\n\t\t\t}\n\t\t}\n\n\t\tif ( ! in_array($type = gettype($index_key), array('integer', 'string', 'NULL'), TRUE))\n\t\t{\n\t\t\tif ($type === 'double')\n\t\t\t{\n\t\t\t\t$index_key = (int) $index_key;\n\t\t\t}\n\t\t\telseif ($type === 'object' && method_exists($index_key, '__toString'))\n\t\t\t{\n\t\t\t\t$index_key = (string) $index_key;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttrigger_error('array_column(): The index key should be either a string or an integer', E_USER_WARNING);\n\t\t\t\treturn FALSE;\n\t\t\t}\n\t\t}\n\n\t\t$result = array();\n\t\tforeach ($array as &$a)\n\t\t{\n\t\t\tif ($column_key === NULL)\n\t\t\t{\n\t\t\t\t$value = $a;\n\t\t\t}\n\t\t\telseif (is_array($a) && array_key_exists($column_key, $a))\n\t\t\t{\n\t\t\t\t$value = $a[$column_key];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ($index_key === NULL OR ! array_key_exists($index_key, $a))\n\t\t\t{\n\t\t\t\t$result[] = $value;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$result[$a[$index_key]] = $value;\n\t\t\t}\n\t\t}\n\n\t\treturn $result;\n\t}\n}\n"
  },
  {
    "path": "system/core/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/database/DB.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Initialize the database\n *\n * @category\tDatabase\n * @author\tEllisLab Dev Team\n * @link\thttps://codeigniter.com/userguide3/database/\n *\n * @param \tstring|string[]\t$params\n */\nfunction &DB($params = '')\n{\n\t// Load the DB config file if a DSN string wasn't passed\n\tif (is_string($params) && strpos($params, '://') === FALSE)\n\t{\n\t\t// Is the config file in the environment folder?\n\t\tif ( ! file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/database.php')\n\t\t\t&& ! file_exists($file_path = APPPATH.'config/database.php'))\n\t\t{\n\t\t\tshow_error('The configuration file database.php does not exist.');\n\t\t}\n\n\t\tinclude($file_path);\n\n\t\t// Make packages contain database config files,\n\t\t// given that the controller instance already exists\n\t\tif (class_exists('CI_Controller', FALSE))\n\t\t{\n\t\t\tforeach (get_instance()->load->get_package_paths() as $path)\n\t\t\t{\n\t\t\t\tif ($path !== APPPATH)\n\t\t\t\t{\n\t\t\t\t\tif (file_exists($file_path = $path.'config/'.ENVIRONMENT.'/database.php'))\n\t\t\t\t\t{\n\t\t\t\t\t\tinclude($file_path);\n\t\t\t\t\t}\n\t\t\t\t\telseif (file_exists($file_path = $path.'config/database.php'))\n\t\t\t\t\t{\n\t\t\t\t\t\tinclude($file_path);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (empty($db))\n\t\t{\n\t\t\tshow_error('No database connection settings were found in the database config file.');\n\t\t}\n\n\t\tif ($params !== '')\n\t\t{\n\t\t\t$active_group = $params;\n\t\t}\n\n\t\tif ( ! isset($active_group))\n\t\t{\n\t\t\tshow_error('You have not specified a database connection group via $active_group in your config/database.php file.');\n\t\t}\n\t\telseif ( ! isset($db[$active_group]))\n\t\t{\n\t\t\tshow_error('You have specified an invalid database connection group ('.$active_group.') in your config/database.php file.');\n\t\t}\n\n\t\t$params = $db[$active_group];\n\t}\n\telseif (is_string($params))\n\t{\n\t\t/**\n\t\t * Parse the URL from the DSN string\n\t\t * Database settings can be passed as discreet\n\t\t * parameters or as a data source name in the first\n\t\t * parameter. DSNs must have this prototype:\n\t\t * $dsn = 'driver://username:password@hostname/database';\n\t\t */\n\t\tif (($dsn = @parse_url($params)) === FALSE)\n\t\t{\n\t\t\tshow_error('Invalid DB Connection String');\n\t\t}\n\n\t\t$params = array(\n\t\t\t'dbdriver'\t=> $dsn['scheme'],\n\t\t\t'hostname'\t=> isset($dsn['host']) ? rawurldecode($dsn['host']) : '',\n\t\t\t'port'\t\t=> isset($dsn['port']) ? rawurldecode($dsn['port']) : '',\n\t\t\t'username'\t=> isset($dsn['user']) ? rawurldecode($dsn['user']) : '',\n\t\t\t'password'\t=> isset($dsn['pass']) ? rawurldecode($dsn['pass']) : '',\n\t\t\t'database'\t=> isset($dsn['path']) ? rawurldecode(substr($dsn['path'], 1)) : ''\n\t\t);\n\n\t\t// Were additional config items set?\n\t\tif (isset($dsn['query']))\n\t\t{\n\t\t\tparse_str($dsn['query'], $extra);\n\n\t\t\tforeach ($extra as $key => $val)\n\t\t\t{\n\t\t\t\tif (is_string($val) && in_array(strtoupper($val), array('TRUE', 'FALSE', 'NULL')))\n\t\t\t\t{\n\t\t\t\t\t$val = var_export($val, TRUE);\n\t\t\t\t}\n\n\t\t\t\t$params[$key] = $val;\n\t\t\t}\n\t\t}\n\t}\n\n\t// No DB specified yet? Beat them senseless...\n\tif (empty($params['dbdriver']))\n\t{\n\t\tshow_error('You have not selected a database type to connect to.');\n\t}\n\n\trequire_once(BASEPATH.'database/DB_driver.php');\n\trequire_once(BASEPATH.'database/DB_query_builder.php');\n\tif ( ! class_exists('CI_DB', FALSE))\n\t{\n\t\t/**\n\t\t * CI_DB\n\t\t *\n\t\t * Acts as an alias for both CI_DB_driver and CI_DB_query_builder.\n\t\t *\n\t\t * @see\tCI_DB_query_builder\n\t\t * @see\tCI_DB_driver\n\t\t */\n\t\tclass CI_DB extends CI_DB_query_builder {}\n\t}\n\n\t// Load the DB driver\n\t$driver_file = BASEPATH.'database/drivers/'.$params['dbdriver'].'/'.$params['dbdriver'].'_driver.php';\n\tfile_exists($driver_file) OR show_error('Invalid DB driver');\n\trequire_once($driver_file);\n\n\t// Load the result classes as well\n\trequire_once(BASEPATH.'database/DB_result.php');\n\trequire_once(BASEPATH.'database/drivers/'.$params['dbdriver'].'/'.$params['dbdriver'].'_result.php');\n\n\t// Instantiate the DB adapter\n\t$driver = 'CI_DB_'.$params['dbdriver'].'_driver';\n\t$DB = new $driver($params);\n\n\t// Check for a subdriver\n\tif ( ! empty($DB->subdriver))\n\t{\n\t\t$driver_file = BASEPATH.'database/drivers/'.$DB->dbdriver.'/subdrivers/'.$DB->dbdriver.'_'.$DB->subdriver.'_driver.php';\n\n\t\tif (file_exists($driver_file))\n\t\t{\n\t\t\trequire_once($driver_file);\n\t\t\t$driver = 'CI_DB_'.$DB->dbdriver.'_'.$DB->subdriver.'_driver';\n\t\t\t$DB = new $driver($params);\n\t\t}\n\t}\n\n\t$DB->initialize();\n\treturn $DB;\n}\n"
  },
  {
    "path": "system/database/DB_cache.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Database Cache Class\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_Cache {\n\n\t/**\n\t * CI Singleton\n\t *\n\t * @var\tobject\n\t */\n\tpublic $CI;\n\n\t/**\n\t * Database object\n\t *\n\t * Allows passing of DB object so that multiple database connections\n\t * and returned DB objects can be supported.\n\t *\n\t * @var\tobject\n\t */\n\tpublic $db;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Constructor\n\t *\n\t * @param\tobject\t&$db\n\t * @return\tvoid\n\t */\n\tpublic function __construct(&$db)\n\t{\n\t\t// Assign the main CI object to $this->CI and load the file helper since we use it a lot\n\t\t$this->CI =& get_instance();\n\t\t$this->db =& $db;\n\t\t$this->CI->load->helper('file');\n\n\t\t$this->check_path();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Cache Directory Path\n\t *\n\t * @param\tstring\t$path\tPath to the cache directory\n\t * @return\tbool\n\t */\n\tpublic function check_path($path = '')\n\t{\n\t\tif ($path === '')\n\t\t{\n\t\t\tif ($this->db->cachedir === '')\n\t\t\t{\n\t\t\t\treturn $this->db->cache_off();\n\t\t\t}\n\n\t\t\t$path = $this->db->cachedir;\n\t\t}\n\n\t\t// Add a trailing slash to the path if needed\n\t\t$path = realpath($path)\n\t\t\t? rtrim(realpath($path), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR\n\t\t\t: rtrim($path, '/').'/';\n\n\t\tif ( ! is_dir($path))\n\t\t{\n\t\t\tlog_message('debug', 'DB cache path error: '.$path);\n\n\t\t\t// If the path is wrong we'll turn off caching\n\t\t\treturn $this->db->cache_off();\n\t\t}\n\n\t\tif ( ! is_really_writable($path))\n\t\t{\n\t\t\tlog_message('debug', 'DB cache dir not writable: '.$path);\n\n\t\t\t// If the path is not really writable we'll turn off caching\n\t\t\treturn $this->db->cache_off();\n\t\t}\n\n\t\t$this->db->cachedir = $path;\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Retrieve a cached query\n\t *\n\t * The URI being requested will become the name of the cache sub-folder.\n\t * An MD5 hash of the SQL statement will become the cache file name.\n\t *\n\t * @param\tstring\t$sql\n\t * @return\tstring\n\t */\n\tpublic function read($sql)\n\t{\n\t\t$segment_one = ($this->CI->uri->segment(1) == FALSE) ? 'default' : $this->CI->uri->segment(1);\n\t\t$segment_two = ($this->CI->uri->segment(2) == FALSE) ? 'index' : $this->CI->uri->segment(2);\n\t\t$filepath = $this->db->cachedir.$segment_one.'+'.$segment_two.'/'.md5($sql);\n\n\t\tif ( ! is_file($filepath) OR FALSE === ($cachedata = file_get_contents($filepath)))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn unserialize($cachedata);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Write a query to a cache file\n\t *\n\t * @param\tstring\t$sql\n\t * @param\tobject\t$object\n\t * @return\tbool\n\t */\n\tpublic function write($sql, $object)\n\t{\n\t\t$segment_one = ($this->CI->uri->segment(1) == FALSE) ? 'default' : $this->CI->uri->segment(1);\n\t\t$segment_two = ($this->CI->uri->segment(2) == FALSE) ? 'index' : $this->CI->uri->segment(2);\n\t\t$dir_path = $this->db->cachedir.$segment_one.'+'.$segment_two.'/';\n\t\t$filename = md5($sql);\n\n\t\tif ( ! is_dir($dir_path) && ! @mkdir($dir_path, 0750))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif (write_file($dir_path.$filename, serialize($object)) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tchmod($dir_path.$filename, 0640);\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Delete cache files within a particular directory\n\t *\n\t * @param\tstring\t$segment_one\n\t * @param\tstring\t$segment_two\n\t * @return\tvoid\n\t */\n\tpublic function delete($segment_one = '', $segment_two = '')\n\t{\n\t\tif ($segment_one === '')\n\t\t{\n\t\t\t$segment_one  = ($this->CI->uri->segment(1) == FALSE) ? 'default' : $this->CI->uri->segment(1);\n\t\t}\n\n\t\tif ($segment_two === '')\n\t\t{\n\t\t\t$segment_two = ($this->CI->uri->segment(2) == FALSE) ? 'index' : $this->CI->uri->segment(2);\n\t\t}\n\n\t\t$dir_path = $this->db->cachedir.$segment_one.'+'.$segment_two.'/';\n\t\tdelete_files($dir_path, TRUE);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Delete all existing cache files\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function delete_all()\n\t{\n\t\tdelete_files($this->db->cachedir, TRUE, TRUE);\n\t}\n\n}\n"
  },
  {
    "path": "system/database/DB_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Database Driver Class\n *\n * This is the platform-independent base DB implementation class.\n * This class will not be called directly. Rather, the adapter\n * class for the specific database will extend and instantiate it.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nabstract class CI_DB_driver {\n\n\t/**\n\t * Data Source Name / Connect string\n\t *\n\t * @var\tstring\n\t */\n\tpublic $dsn;\n\n\t/**\n\t * Username\n\t *\n\t * @var\tstring\n\t */\n\tpublic $username;\n\n\t/**\n\t * Password\n\t *\n\t * @var\tstring\n\t */\n\tpublic $password;\n\n\t/**\n\t * Hostname\n\t *\n\t * @var\tstring\n\t */\n\tpublic $hostname;\n\n\t/**\n\t * Database name\n\t *\n\t * @var\tstring\n\t */\n\tpublic $database;\n\n\t/**\n\t * Database driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $dbdriver\t\t= 'mysqli';\n\n\t/**\n\t * Sub-driver\n\t *\n\t * @used-by\tCI_DB_pdo_driver\n\t * @var\tstring\n\t */\n\tpublic $subdriver;\n\n\t/**\n\t * Table prefix\n\t *\n\t * @var\tstring\n\t */\n\tpublic $dbprefix\t\t= '';\n\n\t/**\n\t * Character set\n\t *\n\t * @var\tstring\n\t */\n\tpublic $char_set\t\t= 'utf8';\n\n\t/**\n\t * Collation\n\t *\n\t * @var\tstring\n\t */\n\tpublic $dbcollat\t\t= 'utf8_general_ci';\n\n\t/**\n\t * Encryption flag/data\n\t *\n\t * @var\tmixed\n\t */\n\tpublic $encrypt\t\t\t= FALSE;\n\n\t/**\n\t * Swap Prefix\n\t *\n\t * @var\tstring\n\t */\n\tpublic $swap_pre\t\t= '';\n\n\t/**\n\t * Database port\n\t *\n\t * @var\tint\n\t */\n\tpublic $port\t\t\t= NULL;\n\n\t/**\n\t * Persistent connection flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $pconnect\t\t= FALSE;\n\n\t/**\n\t * Connection ID\n\t *\n\t * @var\tobject|resource\n\t */\n\tpublic $conn_id\t\t\t= FALSE;\n\n\t/**\n\t * Result ID\n\t *\n\t * @var\tobject|resource\n\t */\n\tpublic $result_id\t\t= FALSE;\n\n\t/**\n\t * Debug flag\n\t *\n\t * Whether to display error messages.\n\t *\n\t * @var\tbool\n\t */\n\tpublic $db_debug\t\t= FALSE;\n\n\t/**\n\t * Benchmark time\n\t *\n\t * @var\tint\n\t */\n\tpublic $benchmark\t\t= 0;\n\n\t/**\n\t * Executed queries count\n\t *\n\t * @var\tint\n\t */\n\tpublic $query_count\t\t= 0;\n\n\t/**\n\t * Bind marker\n\t *\n\t * Character used to identify values in a prepared statement.\n\t *\n\t * @var\tstring\n\t */\n\tpublic $bind_marker\t\t= '?';\n\n\t/**\n\t * Save queries flag\n\t *\n\t * Whether to keep an in-memory history of queries for debugging purposes.\n\t *\n\t * @var\tbool\n\t */\n\tpublic $save_queries\t\t= TRUE;\n\n\t/**\n\t * Queries list\n\t *\n\t * @see\tCI_DB_driver::$save_queries\n\t * @var\tstring[]\n\t */\n\tpublic $queries\t\t\t= array();\n\n\t/**\n\t * Query times\n\t *\n\t * A list of times that queries took to execute.\n\t *\n\t * @var\tarray\n\t */\n\tpublic $query_times\t\t= array();\n\n\t/**\n\t * Data cache\n\t *\n\t * An internal generic value cache.\n\t *\n\t * @var\tarray\n\t */\n\tpublic $data_cache\t\t= array();\n\n\t/**\n\t * Transaction enabled flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $trans_enabled\t\t= TRUE;\n\n\t/**\n\t * Strict transaction mode flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $trans_strict\t\t= TRUE;\n\n\t/**\n\t * Transaction depth level\n\t *\n\t * @var\tint\n\t */\n\tprotected $_trans_depth\t\t= 0;\n\n\t/**\n\t * Transaction status flag\n\t *\n\t * Used with transactions to determine if a rollback should occur.\n\t *\n\t * @var\tbool\n\t */\n\tprotected $_trans_status\t= TRUE;\n\n\t/**\n\t * Transaction failure flag\n\t *\n\t * Used with transactions to determine if a transaction has failed.\n\t *\n\t * @var\tbool\n\t */\n\tprotected $_trans_failure\t= FALSE;\n\n\t/**\n\t * Cache On flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $cache_on\t\t= FALSE;\n\n\t/**\n\t * Cache directory path\n\t *\n\t * @var\tbool\n\t */\n\tpublic $cachedir\t\t= '';\n\n\t/**\n\t * Cache auto-delete flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $cache_autodel\t\t= FALSE;\n\n\t/**\n\t * DB Cache object\n\t *\n\t * @see\tCI_DB_cache\n\t * @var\tobject\n\t */\n\tpublic $CACHE;\n\n\t/**\n\t * Protect identifiers flag\n\t *\n\t * @var\tbool\n\t */\n\tprotected $_protect_identifiers\t\t= TRUE;\n\n\t/**\n\t * List of reserved identifiers\n\t *\n\t * Identifiers that must NOT be escaped.\n\t *\n\t * @var\tstring[]\n\t */\n\tprotected $_reserved_identifiers\t= array('*');\n\n\t/**\n\t * Identifier escape character\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_escape_char = '\"';\n\n\t/**\n\t * ESCAPE statement string\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_like_escape_str = \" ESCAPE '%s' \";\n\n\t/**\n\t * ESCAPE character\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_like_escape_chr = '!';\n\n\t/**\n\t * ORDER BY random keyword\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_random_keyword = array('RAND()', 'RAND(%d)');\n\n\t/**\n\t * COUNT string\n\t *\n\t * @used-by\tCI_DB_driver::count_all()\n\t * @used-by\tCI_DB_query_builder::count_all_results()\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_count_string = 'SELECT COUNT(*) AS ';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * @param\tarray\t$params\n\t * @return\tvoid\n\t */\n\tpublic function __construct($params)\n\t{\n\t\tif (is_array($params))\n\t\t{\n\t\t\tforeach ($params as $key => $val)\n\t\t\t{\n\t\t\t\t$this->$key = $val;\n\t\t\t}\n\t\t}\n\n\t\tlog_message('info', 'Database Driver Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Initialize Database Settings\n\t *\n\t * @return\tvoid\n\t * @throws\tRuntimeException\tIn case of failure\n\t */\n\tpublic function initialize()\n\t{\n\t\t/* If an established connection is available, then there's\n\t\t * no need to connect and select the database.\n\t\t *\n\t\t * Depending on the database driver, conn_id can be either\n\t\t * boolean TRUE, a resource or an object.\n\t\t */\n\t\tif ($this->conn_id)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t// ----------------------------------------------------------------\n\n\t\t// Connect to the database and set the connection ID\n\t\t$this->conn_id = $this->db_connect($this->pconnect);\n\n\t\t// No connection resource? Check if there is a failover else throw an error\n\t\tif ( ! $this->conn_id)\n\t\t{\n\t\t\t// Check if there is a failover set\n\t\t\tif ( ! empty($this->failover) && is_array($this->failover))\n\t\t\t{\n\t\t\t\t// Go over all the failovers\n\t\t\t\tforeach ($this->failover as $failover)\n\t\t\t\t{\n\t\t\t\t\t// Replace the current settings with those of the failover\n\t\t\t\t\tforeach ($failover as $key => $val)\n\t\t\t\t\t{\n\t\t\t\t\t\t$this->$key = $val;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Try to connect\n\t\t\t\t\t$this->conn_id = $this->db_connect($this->pconnect);\n\n\t\t\t\t\t// If a connection is made break the foreach loop\n\t\t\t\t\tif ($this->conn_id)\n\t\t\t\t\t{\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// We still don't have a connection?\n\t\t\tif ( ! $this->conn_id)\n\t\t\t{\n\t\t\t\tthrow new RuntimeException('Unable to connect to the database.');\n\t\t\t}\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * DB connect\n\t *\n\t * This is just a dummy method that all drivers will override.\n\t *\n\t * @return\tmixed\n\t */\n\tpublic function db_connect()\n\t{\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Persistent database connection\n\t *\n\t * @return\tmixed\n\t */\n\tpublic function db_pconnect()\n\t{\n\t\treturn $this->db_connect(TRUE);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Reconnect\n\t *\n\t * Keep / reestablish the db connection if no queries have been\n\t * sent for a length of time exceeding the server's idle timeout.\n\t *\n\t * This is just a dummy method to allow drivers without such\n\t * functionality to not declare it, while others will override it.\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function reconnect()\n\t{\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Select database\n\t *\n\t * This is just a dummy method to allow drivers without such\n\t * functionality to not declare it, while others will override it.\n\t *\n\t * @return\tbool\n\t */\n\tpublic function db_select()\n\t{\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Last error\n\t *\n\t * @return\tarray\n\t */\n\tpublic function error()\n\t{\n\t\treturn array('code' => NULL, 'message' => NULL);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * The name of the platform in use (mysql, mssql, etc...)\n\t *\n\t * @return\tstring\n\t */\n\tpublic function platform()\n\t{\n\t\treturn $this->dbdriver;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Database version number\n\t *\n\t * Returns a string containing the version of the database being used.\n\t * Most drivers will override this method.\n\t *\n\t * @return\tstring\n\t */\n\tpublic function version()\n\t{\n\t\tif (isset($this->data_cache['version']))\n\t\t{\n\t\t\treturn $this->data_cache['version'];\n\t\t}\n\n\t\tif (FALSE === ($sql = $this->_version()))\n\t\t{\n\t\t\treturn ($this->db_debug) ? $this->display_error('db_unsupported_function') : FALSE;\n\t\t}\n\n\t\t$query = $this->query($sql)->row();\n\t\treturn $this->data_cache['version'] = $query->ver;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Version number query string\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _version()\n\t{\n\t\treturn 'SELECT VERSION() AS ver';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Execute the query\n\t *\n\t * Accepts an SQL string as input and returns a result object upon\n\t * successful execution of a \"read\" type query. Returns boolean TRUE\n\t * upon successful execution of a \"write\" type query. Returns boolean\n\t * FALSE upon failure, and if the $db_debug variable is set to TRUE\n\t * will raise an error.\n\t *\n\t * @param\tstring\t$sql\n\t * @param\tarray\t$binds = FALSE\t\tAn array of binding data\n\t * @param\tbool\t$return_object = NULL\n\t * @return\tmixed\n\t */\n\tpublic function query($sql, $binds = FALSE, $return_object = NULL)\n\t{\n\t\tif ($sql === '')\n\t\t{\n\t\t\tlog_message('error', 'Invalid query: '.$sql);\n\t\t\treturn ($this->db_debug) ? $this->display_error('db_invalid_query') : FALSE;\n\t\t}\n\t\telseif ( ! is_bool($return_object))\n\t\t{\n\t\t\t$return_object = ! $this->is_write_type($sql);\n\t\t}\n\n\t\t// Verify table prefix and replace if necessary\n\t\tif ($this->dbprefix !== '' && $this->swap_pre !== '' && $this->dbprefix !== $this->swap_pre)\n\t\t{\n\t\t\t$sql = preg_replace('/(\\W)'.$this->swap_pre.'(\\S+?)/', '\\\\1'.$this->dbprefix.'\\\\2', $sql);\n\t\t}\n\n\t\t// Compile binds if needed\n\t\tif ($binds !== FALSE)\n\t\t{\n\t\t\t$sql = $this->compile_binds($sql, $binds);\n\t\t}\n\n\t\t// Is query caching enabled? If the query is a \"read type\"\n\t\t// we will load the caching class and return the previously\n\t\t// cached query if it exists\n\t\tif ($this->cache_on === TRUE && $return_object === TRUE && $this->_cache_init())\n\t\t{\n\t\t\tif (FALSE !== ($cache = $this->CACHE->read($sql)))\n\t\t\t{\n\t\t\t\treturn $cache;\n\t\t\t}\n\t\t}\n\n\t\t// Save the query for debugging\n\t\tif ($this->save_queries === TRUE)\n\t\t{\n\t\t\t$this->queries[] = $sql;\n\t\t}\n\n\t\t// Start the Query Timer\n\t\t$time_start = microtime(TRUE);\n\n\t\t// Run the Query\n\t\tif (FALSE === ($this->result_id = $this->simple_query($sql)))\n\t\t{\n\t\t\tif ($this->save_queries === TRUE)\n\t\t\t{\n\t\t\t\t$this->query_times[] = 0;\n\t\t\t}\n\n\t\t\t// This will trigger a rollback if transactions are being used\n\t\t\tif ($this->_trans_depth !== 0)\n\t\t\t{\n\t\t\t\t$this->_trans_status = FALSE;\n\t\t\t}\n\n\t\t\t// Grab the error now, as we might run some additional queries before displaying the error\n\t\t\t$error = $this->error();\n\n\t\t\t// Log errors\n\t\t\tlog_message('error', 'Query error: '.$error['message'].' - Invalid query: '.$sql);\n\n\t\t\tif ($this->db_debug)\n\t\t\t{\n\t\t\t\t// We call this function in order to roll-back queries\n\t\t\t\t// if transactions are enabled. If we don't call this here\n\t\t\t\t// the error message will trigger an exit, causing the\n\t\t\t\t// transactions to remain in limbo.\n\t\t\t\twhile ($this->_trans_depth !== 0)\n\t\t\t\t{\n\t\t\t\t\t$trans_depth = $this->_trans_depth;\n\t\t\t\t\t$this->trans_complete();\n\t\t\t\t\tif ($trans_depth === $this->_trans_depth)\n\t\t\t\t\t{\n\t\t\t\t\t\tlog_message('error', 'Database: Failure during an automated transaction commit/rollback!');\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Display errors\n\t\t\t\treturn $this->display_error(array('Error Number: '.$error['code'], $error['message'], $sql));\n\t\t\t}\n\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Stop and aggregate the query time results\n\t\t$time_end = microtime(TRUE);\n\t\t$this->benchmark += $time_end - $time_start;\n\n\t\tif ($this->save_queries === TRUE)\n\t\t{\n\t\t\t$this->query_times[] = $time_end - $time_start;\n\t\t}\n\n\t\t// Increment the query counter\n\t\t$this->query_count++;\n\n\t\t// Will we have a result object instantiated? If not - we'll simply return TRUE\n\t\tif ($return_object !== TRUE)\n\t\t{\n\t\t\t// If caching is enabled we'll auto-cleanup any existing files related to this particular URI\n\t\t\tif ($this->cache_on === TRUE && $this->cache_autodel === TRUE && $this->_cache_init())\n\t\t\t{\n\t\t\t\t$this->CACHE->delete();\n\t\t\t}\n\n\t\t\treturn TRUE;\n\t\t}\n\n\t\t// Instantiate the driver-specific result class\n\t\t$driver\t= 'CI_DB_'.$this->dbdriver.'_result';\n\t\t$RES    = new $driver($this);\n\n\t\t// Is query caching enabled? If so, we'll serialize the\n\t\t// result object and save it to a cache file.\n\t\tif ($this->cache_on === TRUE && $this->_cache_init())\n\t\t{\n\t\t\t// We'll create a new instance of the result object\n\t\t\t// only without the platform specific driver since\n\t\t\t// we can't use it with cached data (the query result\n\t\t\t// resource ID won't be any good once we've cached the\n\t\t\t// result object, so we'll have to compile the data\n\t\t\t// and save it)\n\t\t\t$CR = new CI_DB_result($this);\n\t\t\t$CR->result_object\t= $RES->result_object();\n\t\t\t$CR->result_array\t= $RES->result_array();\n\t\t\t$CR->num_rows\t\t= $RES->num_rows();\n\n\t\t\t// Reset these since cached objects can not utilize resource IDs.\n\t\t\t$CR->conn_id\t\t= NULL;\n\t\t\t$CR->result_id\t\t= NULL;\n\n\t\t\t$this->CACHE->write($sql, $CR);\n\t\t}\n\n\t\treturn $RES;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Simple Query\n\t * This is a simplified version of the query() function. Internally\n\t * we only use it when running transaction commands since they do\n\t * not require all the features of the main query() function.\n\t *\n\t * @param\tstring\tthe sql query\n\t * @return\tmixed\n\t */\n\tpublic function simple_query($sql)\n\t{\n\t\tempty($this->conn_id) && $this->initialize();\n\t\treturn $this->_execute($sql);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Disable Transactions\n\t * This permits transactions to be disabled at run-time.\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function trans_off()\n\t{\n\t\t$this->trans_enabled = FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Enable/disable Transaction Strict Mode\n\t *\n\t * When strict mode is enabled, if you are running multiple groups of\n\t * transactions, if one group fails all subsequent groups will be\n\t * rolled back.\n\t *\n\t * If strict mode is disabled, each group is treated autonomously,\n\t * meaning a failure of one group will not affect any others\n\t *\n\t * @param\tbool\t$mode = TRUE\n\t * @return\tvoid\n\t */\n\tpublic function trans_strict($mode = TRUE)\n\t{\n\t\t$this->trans_strict = is_bool($mode) ? $mode : TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Start Transaction\n\t *\n\t * @param\tbool\t$test_mode = FALSE\n\t * @return\tbool\n\t */\n\tpublic function trans_start($test_mode = FALSE)\n\t{\n\t\tif ( ! $this->trans_enabled)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn $this->trans_begin($test_mode);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Complete Transaction\n\t *\n\t * @return\tbool\n\t */\n\tpublic function trans_complete()\n\t{\n\t\tif ( ! $this->trans_enabled)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// The query() function will set this flag to FALSE in the event that a query failed\n\t\tif ($this->_trans_status === FALSE OR $this->_trans_failure === TRUE)\n\t\t{\n\t\t\t$this->trans_rollback();\n\n\t\t\t// If we are NOT running in strict mode, we will reset\n\t\t\t// the _trans_status flag so that subsequent groups of\n\t\t\t// transactions will be permitted.\n\t\t\tif ($this->trans_strict === FALSE)\n\t\t\t{\n\t\t\t\t$this->_trans_status = TRUE;\n\t\t\t}\n\n\t\t\tlog_message('debug', 'DB Transaction Failure');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn $this->trans_commit();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Lets you retrieve the transaction flag to determine if it has failed\n\t *\n\t * @return\tbool\n\t */\n\tpublic function trans_status()\n\t{\n\t\treturn $this->_trans_status;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns TRUE if a transaction is currently active\n\t *\n\t * @return\tbool\n\t */\n\tpublic function trans_active()\n\t{\n\t\treturn (bool) $this->_trans_depth;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Begin Transaction\n\t *\n\t * @param\tbool\t$test_mode\n\t * @return\tbool\n\t */\n\tpublic function trans_begin($test_mode = FALSE)\n\t{\n\t\tif ( ! $this->trans_enabled)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\t\t// When transactions are nested we only begin/commit/rollback the outermost ones\n\t\telseif ($this->_trans_depth > 0)\n\t\t{\n\t\t\t$this->_trans_depth++;\n\t\t\treturn TRUE;\n\t\t}\n\n\t\t// Reset the transaction failure flag.\n\t\t// If the $test_mode flag is set to TRUE transactions will be rolled back\n\t\t// even if the queries produce a successful result.\n\t\t$this->_trans_failure = ($test_mode === TRUE);\n\n\t\tif ($this->_trans_begin())\n\t\t{\n\t\t\t$this->_trans_status = TRUE;\n\t\t\t$this->_trans_depth++;\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Commit Transaction\n\t *\n\t * @return\tbool\n\t */\n\tpublic function trans_commit()\n\t{\n\t\tif ( ! $this->trans_enabled OR $this->_trans_depth === 0)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\t\t// When transactions are nested we only begin/commit/rollback the outermost ones\n\t\telseif ($this->_trans_depth > 1 OR $this->_trans_commit())\n\t\t{\n\t\t\t$this->_trans_depth--;\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Rollback Transaction\n\t *\n\t * @return\tbool\n\t */\n\tpublic function trans_rollback()\n\t{\n\t\tif ( ! $this->trans_enabled OR $this->_trans_depth === 0)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\t\t// When transactions are nested we only begin/commit/rollback the outermost ones\n\t\telseif ($this->_trans_depth > 1 OR $this->_trans_rollback())\n\t\t{\n\t\t\t$this->_trans_depth--;\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Compile Bindings\n\t *\n\t * @param\tstring\tthe sql statement\n\t * @param\tarray\tan array of bind data\n\t * @return\tstring\n\t */\n\tpublic function compile_binds($sql, $binds)\n\t{\n\t\tif (empty($this->bind_marker) OR strpos($sql, $this->bind_marker) === FALSE)\n\t\t{\n\t\t\treturn $sql;\n\t\t}\n\t\telseif ( ! is_array($binds))\n\t\t{\n\t\t\t$binds = array($binds);\n\t\t\t$bind_count = 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Make sure we're using numeric keys\n\t\t\t$binds = array_values($binds);\n\t\t\t$bind_count = count($binds);\n\t\t}\n\n\t\t// We'll need the marker length later\n\t\t$ml = strlen($this->bind_marker);\n\n\t\t// Make sure not to replace a chunk inside a string that happens to match the bind marker\n\t\tif ($c = preg_match_all(\"/'[^']*'|\\\"[^\\\"]*\\\"/i\", $sql, $matches))\n\t\t{\n\t\t\t$c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i',\n\t\t\t\tstr_replace($matches[0],\n\t\t\t\t\tstr_replace($this->bind_marker, str_repeat(' ', $ml), $matches[0]),\n\t\t\t\t\t$sql, $c),\n\t\t\t\t$matches, PREG_OFFSET_CAPTURE);\n\n\t\t\t// Bind values' count must match the count of markers in the query\n\t\t\tif ($bind_count !== $c)\n\t\t\t{\n\t\t\t\treturn $sql;\n\t\t\t}\n\t\t}\n\t\telseif (($c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i', $sql, $matches, PREG_OFFSET_CAPTURE)) !== $bind_count)\n\t\t{\n\t\t\treturn $sql;\n\t\t}\n\n\t\tdo\n\t\t{\n\t\t\t$c--;\n\t\t\t$escaped_value = $this->escape($binds[$c]);\n\t\t\tif (is_array($escaped_value))\n\t\t\t{\n\t\t\t\t$escaped_value = '('.implode(',', $escaped_value).')';\n\t\t\t}\n\t\t\t$sql = substr_replace($sql, $escaped_value, $matches[0][$c][1], $ml);\n\t\t}\n\t\twhile ($c !== 0);\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Determines if a query is a \"write\" type.\n\t *\n\t * @param\tstring\tAn SQL query string\n\t * @return\tbool\n\t */\n\tpublic function is_write_type($sql)\n\t{\n\t\treturn (bool) preg_match('/^\\s*\"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD|COPY|ALTER|RENAME|GRANT|REVOKE|LOCK|UNLOCK|REINDEX|MERGE)\\s/i', $sql);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Calculate the aggregate query elapsed time\n\t *\n\t * @param\tint\tThe number of decimal places\n\t * @return\tstring\n\t */\n\tpublic function elapsed_time($decimals = 6)\n\t{\n\t\treturn number_format($this->benchmark, $decimals);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns the total number of queries\n\t *\n\t * @return\tint\n\t */\n\tpublic function total_queries()\n\t{\n\t\treturn $this->query_count;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns the last query that was executed\n\t *\n\t * @return\tstring\n\t */\n\tpublic function last_query()\n\t{\n\t\treturn end($this->queries);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * \"Smart\" Escape String\n\t *\n\t * Escapes data based on type\n\t * Sets boolean and null types\n\t *\n\t * @param\tstring\n\t * @return\tmixed\n\t */\n\tpublic function escape($str)\n\t{\n\t\tif (is_array($str))\n\t\t{\n\t\t\t$str = array_map(array(&$this, 'escape'), $str);\n\t\t\treturn $str;\n\t\t}\n\t\telseif (is_string($str) OR (is_object($str) && method_exists($str, '__toString')))\n\t\t{\n\t\t\treturn \"'\".$this->escape_str($str).\"'\";\n\t\t}\n\t\telseif (is_bool($str))\n\t\t{\n\t\t\treturn ($str === FALSE) ? 0 : 1;\n\t\t}\n\t\telseif ($str === NULL)\n\t\t{\n\t\t\treturn 'NULL';\n\t\t}\n\n\t\treturn $str;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Escape String\n\t *\n\t * @param\tstring|string[]\t$str\tInput string\n\t * @param\tbool\t$like\tWhether or not the string will be used in a LIKE condition\n\t * @return\tstring\n\t */\n\tpublic function escape_str($str, $like = FALSE)\n\t{\n\t\tif (is_array($str))\n\t\t{\n\t\t\tforeach ($str as $key => $val)\n\t\t\t{\n\t\t\t\t$str[$key] = $this->escape_str($val, $like);\n\t\t\t}\n\n\t\t\treturn $str;\n\t\t}\n\n\t\t$str = $this->_escape_str($str);\n\n\t\t// escape LIKE condition wildcards\n\t\tif ($like === TRUE)\n\t\t{\n\t\t\treturn str_replace(\n\t\t\t\tarray($this->_like_escape_chr, '%', '_'),\n\t\t\t\tarray($this->_like_escape_chr.$this->_like_escape_chr, $this->_like_escape_chr.'%', $this->_like_escape_chr.'_'),\n\t\t\t\t$str\n\t\t\t);\n\t\t}\n\n\t\treturn $str;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Escape LIKE String\n\t *\n\t * Calls the individual driver for platform\n\t * specific escaping for LIKE conditions\n\t *\n\t * @param\tstring|string[]\n\t * @return\tmixed\n\t */\n\tpublic function escape_like_str($str)\n\t{\n\t\treturn $this->escape_str($str, TRUE);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Platform-dependent string escape\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tprotected function _escape_str($str)\n\t{\n\t\treturn str_replace(\"'\", \"''\", remove_invisible_characters($str, FALSE));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Primary\n\t *\n\t * Retrieves the primary key. It assumes that the row in the first\n\t * position is the primary key\n\t *\n\t * @param\tstring\t$table\tTable name\n\t * @return\tstring\n\t */\n\tpublic function primary($table)\n\t{\n\t\t$fields = $this->list_fields($table);\n\t\treturn is_array($fields) ? current($fields) : FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * \"Count All\" query\n\t *\n\t * Generates a platform-specific query string that counts all records in\n\t * the specified database\n\t *\n\t * @param\tstring\n\t * @return\tint\n\t */\n\tpublic function count_all($table = '')\n\t{\n\t\tif ($table === '')\n\t\t{\n\t\t\treturn 0;\n\t\t}\n\n\t\t$query = $this->query($this->_count_string.$this->escape_identifiers('numrows').' FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE));\n\t\tif ($query->num_rows() === 0)\n\t\t{\n\t\t\treturn 0;\n\t\t}\n\n\t\t$query = $query->row();\n\t\t$this->_reset_select();\n\t\treturn (int) $query->numrows;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns an array of table names\n\t *\n\t * @param\tstring\t$constrain_by_prefix = FALSE\n\t * @return\tarray\n\t */\n\tpublic function list_tables($constrain_by_prefix = FALSE)\n\t{\n\t\t// Is there a cached result?\n\t\tif (isset($this->data_cache['table_names']))\n\t\t{\n\t\t\treturn $this->data_cache['table_names'];\n\t\t}\n\n\t\tif (FALSE === ($sql = $this->_list_tables($constrain_by_prefix)))\n\t\t{\n\t\t\treturn ($this->db_debug) ? $this->display_error('db_unsupported_function') : FALSE;\n\t\t}\n\n\t\t$this->data_cache['table_names'] = array();\n\t\t$query = $this->query($sql);\n\n\t\tforeach ($query->result_array() as $row)\n\t\t{\n\t\t\t// Do we know from which column to get the table name?\n\t\t\tif ( ! isset($key))\n\t\t\t{\n\t\t\t\tif (isset($row['table_name']))\n\t\t\t\t{\n\t\t\t\t\t$key = 'table_name';\n\t\t\t\t}\n\t\t\t\telseif (isset($row['TABLE_NAME']))\n\t\t\t\t{\n\t\t\t\t\t$key = 'TABLE_NAME';\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/* We have no other choice but to just get the first element's key.\n\t\t\t\t\t * Due to array_shift() accepting its argument by reference, if\n\t\t\t\t\t * E_STRICT is on, this would trigger a warning. So we'll have to\n\t\t\t\t\t * assign it first.\n\t\t\t\t\t */\n\t\t\t\t\t$key = array_keys($row);\n\t\t\t\t\t$key = array_shift($key);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$this->data_cache['table_names'][] = $row[$key];\n\t\t}\n\n\t\treturn $this->data_cache['table_names'];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Determine if a particular table exists\n\t *\n\t * @param\tstring\t$table_name\n\t * @return\tbool\n\t */\n\tpublic function table_exists($table_name)\n\t{\n\t\treturn in_array($this->protect_identifiers($table_name, TRUE, FALSE, FALSE), $this->list_tables());\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch Field Names\n\t *\n\t * @param\tstring\t$table\tTable name\n\t * @return\tarray\n\t */\n\tpublic function list_fields($table)\n\t{\n\t\tif (FALSE === ($sql = $this->_list_columns($table)))\n\t\t{\n\t\t\treturn ($this->db_debug) ? $this->display_error('db_unsupported_function') : FALSE;\n\t\t}\n\n\t\t$query = $this->query($sql);\n\t\t$fields = array();\n\n\t\tforeach ($query->result_array() as $row)\n\t\t{\n\t\t\t// Do we know from where to get the column's name?\n\t\t\tif ( ! isset($key))\n\t\t\t{\n\t\t\t\tif (isset($row['column_name']))\n\t\t\t\t{\n\t\t\t\t\t$key = 'column_name';\n\t\t\t\t}\n\t\t\t\telseif (isset($row['COLUMN_NAME']))\n\t\t\t\t{\n\t\t\t\t\t$key = 'COLUMN_NAME';\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// We have no other choice but to just get the first element's key.\n\t\t\t\t\t$key = key($row);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$fields[] = $row[$key];\n\t\t}\n\n\t\treturn $fields;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Determine if a particular field exists\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function field_exists($field_name, $table_name)\n\t{\n\t\treturn in_array($field_name, $this->list_fields($table_name));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns an object with field data\n\t *\n\t * @param\tstring\t$table\tthe table name\n\t * @return\tarray\n\t */\n\tpublic function field_data($table)\n\t{\n\t\t$query = $this->query($this->_field_data($this->protect_identifiers($table, TRUE, NULL, FALSE)));\n\t\treturn ($query) ? $query->field_data() : FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Escape the SQL Identifiers\n\t *\n\t * This function escapes column and table names\n\t *\n\t * @param\tmixed\t$item\tIdentifier to escape\n\t * @param\tbool\t$split\tWhether to split identifiers when a dot is encountered\n\t * @return\tmixed\n\t */\n\tpublic function escape_identifiers($item, $split = TRUE)\n\t{\n\t\tif ($this->_escape_char === '' OR empty($item) OR in_array($item, $this->_reserved_identifiers))\n\t\t{\n\t\t\treturn $item;\n\t\t}\n\t\telseif (is_array($item))\n\t\t{\n\t\t\tforeach ($item as $key => $value)\n\t\t\t{\n\t\t\t\t$item[$key] = $this->escape_identifiers($value);\n\t\t\t}\n\n\t\t\treturn $item;\n\t\t}\n\t\t// Avoid breaking functions and literal values inside queries\n\t\telseif (ctype_digit($item) OR $item[0] === \"'\" OR ($this->_escape_char !== '\"' && $item[0] === '\"') OR strpos($item, '(') !== FALSE)\n\t\t{\n\t\t\treturn $item;\n\t\t}\n\n\t\tstatic $preg_ec;\n\n\t\tif (empty($preg_ec))\n\t\t{\n\t\t\tif (is_array($this->_escape_char))\n\t\t\t{\n\t\t\t\t$preg_ec = array(\n\t\t\t\t\tpreg_quote($this->_escape_char[0]),\n\t\t\t\t\tpreg_quote($this->_escape_char[1]),\n\t\t\t\t\t$this->_escape_char[0],\n\t\t\t\t\t$this->_escape_char[1]\n\t\t\t\t);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$preg_ec[0] = $preg_ec[1] = preg_quote($this->_escape_char);\n\t\t\t\t$preg_ec[2] = $preg_ec[3] = $this->_escape_char;\n\t\t\t}\n\t\t}\n\n\t\tforeach ($this->_reserved_identifiers as $id)\n\t\t{\n\t\t\tif (strpos($item, '.'.$id) !== FALSE)\n\t\t\t{\n\t\t\t\treturn preg_replace('#'.$preg_ec[0].'?([^'.$preg_ec[1].'\\.]+)'.$preg_ec[1].'?\\.#i', $preg_ec[2].'$1'.$preg_ec[3].'.', $item);\n\t\t\t}\n\t\t}\n\n\t\t$dot = ($split !== FALSE) ? '\\.' : '';\n\n\t\treturn preg_replace('#'.$preg_ec[0].'?([^'.$preg_ec[1].$dot.']+)'.$preg_ec[1].'?(\\.)?#i', $preg_ec[2].'$1'.$preg_ec[3].'$2', $item);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Generate an insert string\n\t *\n\t * @param\tstring\tthe table upon which the query will be performed\n\t * @param\tarray\tan associative array data of key/values\n\t * @return\tstring\n\t */\n\tpublic function insert_string($table, $data)\n\t{\n\t\t$fields = $values = array();\n\n\t\tforeach ($data as $key => $val)\n\t\t{\n\t\t\t$fields[] = $this->escape_identifiers($key);\n\t\t\t$values[] = $this->escape($val);\n\t\t}\n\n\t\treturn $this->_insert($this->protect_identifiers($table, TRUE, NULL, FALSE), $fields, $values);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert statement\n\t *\n\t * Generates a platform-specific insert string from the supplied data\n\t *\n\t * @param\tstring\tthe table name\n\t * @param\tarray\tthe insert keys\n\t * @param\tarray\tthe insert values\n\t * @return\tstring\n\t */\n\tprotected function _insert($table, $keys, $values)\n\t{\n\t\treturn 'INSERT INTO '.$table.' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Generate an update string\n\t *\n\t * @param\tstring\tthe table upon which the query will be performed\n\t * @param\tarray\tan associative array data of key/values\n\t * @param\tmixed\tthe \"where\" statement\n\t * @return\tstring\n\t */\n\tpublic function update_string($table, $data, $where)\n\t{\n\t\tif (empty($where))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$this->where($where);\n\n\t\t$fields = array();\n\t\tforeach ($data as $key => $val)\n\t\t{\n\t\t\t$fields[$this->protect_identifiers($key)] = $this->escape($val);\n\t\t}\n\n\t\t$sql = $this->_update($this->protect_identifiers($table, TRUE, NULL, FALSE), $fields);\n\t\t$this->_reset_write();\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Update statement\n\t *\n\t * Generates a platform-specific update string from the supplied data\n\t *\n\t * @param\tstring\tthe table name\n\t * @param\tarray\tthe update data\n\t * @return\tstring\n\t */\n\tprotected function _update($table, $values)\n\t{\n\t\tforeach ($values as $key => $val)\n\t\t{\n\t\t\t$valstr[] = $key.' = '.$val;\n\t\t}\n\n\t\treturn 'UPDATE '.$table.' SET '.implode(', ', $valstr)\n\t\t\t.$this->_compile_wh('qb_where')\n\t\t\t.$this->_compile_order_by()\n\t\t\t.($this->qb_limit !== FALSE ? ' LIMIT '.$this->qb_limit : '');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Tests whether the string has an SQL operator\n\t *\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tprotected function _has_operator($str)\n\t{\n\t\treturn (bool) preg_match('/(<|>|!|=|\\sIS NULL|\\sIS NOT NULL|\\sEXISTS|\\sBETWEEN|\\sLIKE|\\sIN\\s*\\(|\\s)/i', trim($str));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns the SQL string operator\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tprotected function _get_operator($str)\n\t{\n\t\tstatic $_operators;\n\n\t\tif (empty($_operators))\n\t\t{\n\t\t\t$_les = ($this->_like_escape_str !== '')\n\t\t\t\t? '\\s+'.preg_quote(trim(sprintf($this->_like_escape_str, $this->_like_escape_chr)), '/')\n\t\t\t\t: '';\n\t\t\t$_operators = array(\n\t\t\t\t'\\s*(?:<|>|!)?=\\s*',             // =, <=, >=, !=\n\t\t\t\t'\\s*<>?\\s*',                     // <, <>\n\t\t\t\t'\\s*>\\s*',                       // >\n\t\t\t\t'\\s+IS NULL',                    // IS NULL\n\t\t\t\t'\\s+IS NOT NULL',                // IS NOT NULL\n\t\t\t\t'\\s+EXISTS\\s*\\(.*\\)',        // EXISTS(sql)\n\t\t\t\t'\\s+NOT EXISTS\\s*\\(.*\\)',    // NOT EXISTS(sql)\n\t\t\t\t'\\s+BETWEEN\\s+',                 // BETWEEN value AND value\n\t\t\t\t'\\s+NOT BETWEEN\\s+',             // NOT BETWEEN value AND value\n\t\t\t\t'\\s+IN\\s*\\(.*\\)',            // IN(list)\n\t\t\t\t'\\s+NOT IN\\s*\\(.*\\)',        // NOT IN (list)\n\t\t\t\t'\\s+LIKE\\s+\\S.*('.$_les.')?',    // LIKE 'expr'[ ESCAPE '%s']\n\t\t\t\t'\\s+NOT LIKE\\s+\\S.*('.$_les.')?' // NOT LIKE 'expr'[ ESCAPE '%s']\n\t\t\t);\n\n\t\t}\n\n\t\treturn preg_match('/'.implode('|', $_operators).'/i', $str, $match)\n\t\t\t? $match[0] : FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Enables a native PHP function to be run, using a platform agnostic wrapper.\n\t *\n\t * @param\tstring\t$function\tFunction name\n\t * @return\tmixed\n\t */\n\tpublic function call_function($function)\n\t{\n\t\t$driver = ($this->dbdriver === 'postgre') ? 'pg_' : $this->dbdriver.'_';\n\n\t\tif (FALSE === strpos($driver, $function))\n\t\t{\n\t\t\t$function = $driver.$function;\n\t\t}\n\n\t\tif ( ! function_exists($function))\n\t\t{\n\t\t\treturn ($this->db_debug) ? $this->display_error('db_unsupported_function') : FALSE;\n\t\t}\n\n\t\treturn (func_num_args() > 1)\n\t\t\t? call_user_func_array($function, array_slice(func_get_args(), 1))\n\t\t\t: call_user_func($function);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Cache Directory Path\n\t *\n\t * @param\tstring\tthe path to the cache directory\n\t * @return\tvoid\n\t */\n\tpublic function cache_set_path($path = '')\n\t{\n\t\t$this->cachedir = $path;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Enable Query Caching\n\t *\n\t * @return\tbool\tcache_on value\n\t */\n\tpublic function cache_on()\n\t{\n\t\treturn $this->cache_on = TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Disable Query Caching\n\t *\n\t * @return\tbool\tcache_on value\n\t */\n\tpublic function cache_off()\n\t{\n\t\treturn $this->cache_on = FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Delete the cache files associated with a particular URI\n\t *\n\t * @param\tstring\t$segment_one = ''\n\t * @param\tstring\t$segment_two = ''\n\t * @return\tbool\n\t */\n\tpublic function cache_delete($segment_one = '', $segment_two = '')\n\t{\n\t\treturn $this->_cache_init()\n\t\t\t? $this->CACHE->delete($segment_one, $segment_two)\n\t\t\t: FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Delete All cache files\n\t *\n\t * @return\tbool\n\t */\n\tpublic function cache_delete_all()\n\t{\n\t\treturn $this->_cache_init()\n\t\t\t? $this->CACHE->delete_all()\n\t\t\t: FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Initialize the Cache Class\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _cache_init()\n\t{\n\t\tif ( ! class_exists('CI_DB_Cache', FALSE))\n\t\t{\n\t\t\trequire_once(BASEPATH.'database/DB_cache.php');\n\t\t}\n\t\telseif (is_object($this->CACHE))\n\t\t{\n\t\t\treturn TRUE;\n\t\t}\n\n\t\t$this->CACHE = new CI_DB_Cache($this); // pass db object to support multiple db connections and returned db objects\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Close DB Connection\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function close()\n\t{\n\t\tif ($this->conn_id)\n\t\t{\n\t\t\t$this->_close();\n\t\t\t$this->conn_id = FALSE;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Close DB Connection\n\t *\n\t * This method would be overridden by most of the drivers.\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _close()\n\t{\n\t\t$this->conn_id = FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Display an error message\n\t *\n\t * @param\tstring\tthe error message\n\t * @param\tstring\tany \"swap\" values\n\t * @param\tbool\twhether to localize the message\n\t * @return\tstring\tsends the application/views/errors/error_db.php template\n\t */\n\tpublic function display_error($error = '', $swap = '', $native = FALSE)\n\t{\n\t\t$LANG =& load_class('Lang', 'core');\n\t\t$LANG->load('db');\n\n\t\t$heading = $LANG->line('db_error_heading');\n\n\t\tif ($native === TRUE)\n\t\t{\n\t\t\t$message = (array) $error;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$message = is_array($error) ? $error : array(str_replace('%s', $swap, $LANG->line($error)));\n\t\t}\n\n\t\t// Find the most likely culprit of the error by going through\n\t\t// the backtrace until the source file is no longer in the\n\t\t// database folder.\n\t\t$trace = debug_backtrace();\n\t\tforeach ($trace as $call)\n\t\t{\n\t\t\tif (isset($call['file'], $call['class']))\n\t\t\t{\n\t\t\t\t// We'll need this on Windows, as APPPATH and BASEPATH will always use forward slashes\n\t\t\t\tif (DIRECTORY_SEPARATOR !== '/')\n\t\t\t\t{\n\t\t\t\t\t$call['file'] = str_replace('\\\\', '/', $call['file']);\n\t\t\t\t}\n\n\t\t\t\tif (strpos($call['file'], BASEPATH.'database') === FALSE && strpos($call['class'], 'Loader') === FALSE)\n\t\t\t\t{\n\t\t\t\t\t// Found it - use a relative path for safety\n\t\t\t\t\t$message[] = 'Filename: '.str_replace(array(APPPATH, BASEPATH), '', $call['file']);\n\t\t\t\t\t$message[] = 'Line Number: '.$call['line'];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$error =& load_class('Exceptions', 'core');\n\t\techo $error->show_error($heading, $message, 'error_db');\n\t\texit(8); // EXIT_DATABASE\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Protect Identifiers\n\t *\n\t * This function is used extensively by the Query Builder class, and by\n\t * a couple functions in this class.\n\t * It takes a column or table name (optionally with an alias) and inserts\n\t * the table prefix onto it. Some logic is necessary in order to deal with\n\t * column names that include the path. Consider a query like this:\n\t *\n\t * SELECT hostname.database.table.column AS c FROM hostname.database.table\n\t *\n\t * Or a query with aliasing:\n\t *\n\t * SELECT m.member_id, m.member_name FROM members AS m\n\t *\n\t * Since the column name can include up to four segments (host, DB, table, column)\n\t * or also have an alias prefix, we need to do a bit of work to figure this out and\n\t * insert the table prefix (if it exists) in the proper position, and escape only\n\t * the correct identifiers.\n\t *\n\t * @param\tstring\n\t * @param\tbool\n\t * @param\tmixed\n\t * @param\tbool\n\t * @return\tstring\n\t */\n\tpublic function protect_identifiers($item, $prefix_single = FALSE, $protect_identifiers = NULL, $field_exists = TRUE)\n\t{\n\t\tif ( ! is_bool($protect_identifiers))\n\t\t{\n\t\t\t$protect_identifiers = $this->_protect_identifiers;\n\t\t}\n\n\t\tif (is_array($item))\n\t\t{\n\t\t\t$escaped_array = array();\n\t\t\tforeach ($item as $k => $v)\n\t\t\t{\n\t\t\t\t$escaped_array[$this->protect_identifiers($k)] = $this->protect_identifiers($v, $prefix_single, $protect_identifiers, $field_exists);\n\t\t\t}\n\n\t\t\treturn $escaped_array;\n\t\t}\n\n\t\t// This is basically a bug fix for queries that use MAX, MIN, etc.\n\t\t// If a parenthesis is found we know that we do not need to\n\t\t// escape the data or add a prefix. There's probably a more graceful\n\t\t// way to deal with this, but I'm not thinking of it -- Rick\n\t\t//\n\t\t// Added exception for single quotes as well, we don't want to alter\n\t\t// literal strings. -- Narf\n\t\tif (strcspn($item, \"()'\") !== strlen($item))\n\t\t{\n\t\t\treturn $item;\n\t\t}\n\n\t\t// Convert tabs or multiple spaces into single spaces\n\t\t$item = preg_replace('/\\s+/', ' ', trim($item));\n\n\t\t// If the item has an alias declaration we remove it and set it aside.\n\t\t// Note: strripos() is used in order to support spaces in table names\n\t\tif ($offset = strripos($item, ' AS '))\n\t\t{\n\t\t\t$alias = ($protect_identifiers)\n\t\t\t\t? substr($item, $offset, 4).$this->escape_identifiers(substr($item, $offset + 4), FALSE)\n\t\t\t\t: substr($item, $offset);\n\t\t\t$item = substr($item, 0, $offset);\n\t\t}\n\t\telseif ($offset = strrpos($item, ' '))\n\t\t{\n\t\t\t$alias = ($protect_identifiers)\n\t\t\t\t? ' '.$this->escape_identifiers(substr($item, $offset + 1), FALSE)\n\t\t\t\t: substr($item, $offset);\n\t\t\t$item = substr($item, 0, $offset);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$alias = '';\n\t\t}\n\n\t\t// Break the string apart if it contains periods, then insert the table prefix\n\t\t// in the correct location, assuming the period doesn't indicate that we're dealing\n\t\t// with an alias. While we're at it, we will escape the components\n\t\tif (strpos($item, '.') !== FALSE)\n\t\t{\n\t\t\t$parts = explode('.', $item);\n\n\t\t\t// Does the first segment of the exploded item match\n\t\t\t// one of the aliases previously identified? If so,\n\t\t\t// we have nothing more to do other than escape the item\n\t\t\t//\n\t\t\t// NOTE: The ! empty() condition prevents this method\n\t\t\t//       from breaking when QB isn't enabled.\n\t\t\tif ( ! empty($this->qb_aliased_tables) && in_array($parts[0], $this->qb_aliased_tables))\n\t\t\t{\n\t\t\t\tif ($protect_identifiers === TRUE)\n\t\t\t\t{\n\t\t\t\t\tforeach ($parts as $key => $val)\n\t\t\t\t\t{\n\t\t\t\t\t\tif ( ! in_array($val, $this->_reserved_identifiers))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t$parts[$key] = $this->escape_identifiers($val);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t$item = implode('.', $parts);\n\t\t\t\t}\n\n\t\t\t\treturn $item.$alias;\n\t\t\t}\n\n\t\t\t// Is there a table prefix defined in the config file? If not, no need to do anything\n\t\t\tif ($this->dbprefix !== '')\n\t\t\t{\n\t\t\t\t// We now add the table prefix based on some logic.\n\t\t\t\t// Do we have 4 segments (hostname.database.table.column)?\n\t\t\t\t// If so, we add the table prefix to the column name in the 3rd segment.\n\t\t\t\tif (isset($parts[3]))\n\t\t\t\t{\n\t\t\t\t\t$i = 2;\n\t\t\t\t}\n\t\t\t\t// Do we have 3 segments (database.table.column)?\n\t\t\t\t// If so, we add the table prefix to the column name in 2nd position\n\t\t\t\telseif (isset($parts[2]))\n\t\t\t\t{\n\t\t\t\t\t$i = 1;\n\t\t\t\t}\n\t\t\t\t// Do we have 2 segments (table.column)?\n\t\t\t\t// If so, we add the table prefix to the column name in 1st segment\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$i = 0;\n\t\t\t\t}\n\n\t\t\t\t// This flag is set when the supplied $item does not contain a field name.\n\t\t\t\t// This can happen when this function is being called from a JOIN.\n\t\t\t\tif ($field_exists === FALSE)\n\t\t\t\t{\n\t\t\t\t\t$i++;\n\t\t\t\t}\n\n\t\t\t\t// dbprefix may've already been applied, with or without the identifier escaped\n\t\t\t\t$ec = '(?<ec>'.preg_quote(is_array($this->_escape_char) ? $this->_escape_char[0] : $this->_escape_char).')?';\n\t\t\t\tisset($ec[0]) && $ec .= '?'; // Just in case someone has disabled escaping by forcing an empty escape character\n\n\t\t\t\t// Verify table prefix and replace if necessary\n\t\t\t\tif ($this->swap_pre !== '' && preg_match('#^'.$ec.preg_quote($this->swap_pre).'#', $parts[$i]))\n\t\t\t\t{\n\t\t\t\t\t$parts[$i] = preg_replace('#^'.$ec.preg_quote($this->swap_pre).'(\\S+?)#', '\\\\1'.$this->dbprefix.'\\\\2', $parts[$i]);\n\t\t\t\t}\n\t\t\t\t// We only add the table prefix if it does not already exist\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tpreg_match('#^'.$ec.preg_quote($this->dbprefix).'#', $parts[$i]) OR $parts[$i] = $this->dbprefix.$parts[$i];\n\t\t\t\t}\n\n\t\t\t\t// Put the parts back together\n\t\t\t\t$item = implode('.', $parts);\n\t\t\t}\n\n\t\t\tif ($protect_identifiers === TRUE)\n\t\t\t{\n\t\t\t\t$item = $this->escape_identifiers($item);\n\t\t\t}\n\n\t\t\treturn $item.$alias;\n\t\t}\n\n\t\t// Is there a table prefix? If not, no need to insert it\n\t\tif ($this->dbprefix !== '')\n\t\t{\n\t\t\t// Verify table prefix and replace if necessary\n\t\t\tif ($this->swap_pre !== '' && strpos($item, $this->swap_pre) === 0)\n\t\t\t{\n\t\t\t\t$item = preg_replace('/^'.$this->swap_pre.'(\\S+?)/', $this->dbprefix.'\\\\1', $item);\n\t\t\t}\n\t\t\t// Do we prefix an item with no segments?\n\t\t\telseif ($prefix_single === TRUE && strpos($item, $this->dbprefix) !== 0)\n\t\t\t{\n\t\t\t\t$item = $this->dbprefix.$item;\n\t\t\t}\n\t\t}\n\n\t\tif ($protect_identifiers === TRUE && ! in_array($item, $this->_reserved_identifiers))\n\t\t{\n\t\t\t$item = $this->escape_identifiers($item);\n\t\t}\n\n\t\treturn $item.$alias;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Dummy method that allows Query Builder class to be disabled\n\t * and keep count_all() working.\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _reset_select()\n\t{\n\t}\n\n}\n"
  },
  {
    "path": "system/database/DB_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Database Forge Class\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nabstract class CI_DB_forge {\n\n\t/**\n\t * Database object\n\t *\n\t * @var\tobject\n\t */\n\tprotected $db;\n\n\t/**\n\t * Fields data\n\t *\n\t * @var\tarray\n\t */\n\tpublic $fields\t\t= array();\n\n\t/**\n\t * Keys data\n\t *\n\t * @var\tarray\n\t */\n\tpublic $keys\t\t= array();\n\n\t/**\n\t * Primary Keys data\n\t *\n\t * @var\tarray\n\t */\n\tpublic $primary_keys\t= array();\n\n\t/**\n\t * Database character set\n\t *\n\t * @var\tstring\n\t */\n\tpublic $db_char_set\t= '';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * CREATE DATABASE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_database\t= 'CREATE DATABASE %s';\n\n\t/**\n\t * DROP DATABASE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_drop_database\t= 'DROP DATABASE %s';\n\n\t/**\n\t * CREATE TABLE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_table\t= \"%s %s (%s\\n)\";\n\n\t/**\n\t * CREATE TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_table_if\t= 'CREATE TABLE IF NOT EXISTS';\n\n\t/**\n\t * CREATE TABLE keys flag\n\t *\n\t * Whether table keys are created from within the\n\t * CREATE TABLE statement.\n\t *\n\t * @var\tbool\n\t */\n\tprotected $_create_table_keys\t= FALSE;\n\n\t/**\n\t * DROP TABLE IF EXISTS statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_drop_table_if\t= 'DROP TABLE IF EXISTS';\n\n\t/**\n\t * RENAME TABLE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_rename_table\t= 'ALTER TABLE %s RENAME TO %s;';\n\n\t/**\n\t * UNSIGNED support\n\t *\n\t * @var\tbool|array\n\t */\n\tprotected $_unsigned\t\t= TRUE;\n\n\t/**\n\t * NULL value representation in CREATE/ALTER TABLE statements\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_null\t\t= '';\n\n\t/**\n\t * DEFAULT value representation in CREATE/ALTER TABLE statements\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_default\t\t= ' DEFAULT ';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * @param\tobject\t&$db\tDatabase object\n\t * @return\tvoid\n\t */\n\tpublic function __construct(&$db)\n\t{\n\t\t$this->db =& $db;\n\t\tlog_message('info', 'Database Forge Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Create database\n\t *\n\t * @param\tstring\t$db_name\n\t * @return\tbool\n\t */\n\tpublic function create_database($db_name)\n\t{\n\t\tif ($this->_create_database === FALSE)\n\t\t{\n\t\t\treturn ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;\n\t\t}\n\t\telseif ( ! $this->db->query(sprintf($this->_create_database, $this->db->escape_identifiers($db_name), $this->db->char_set, $this->db->dbcollat)))\n\t\t{\n\t\t\treturn ($this->db->db_debug) ? $this->db->display_error('db_unable_to_drop') : FALSE;\n\t\t}\n\n\t\tif ( ! empty($this->db->data_cache['db_names']))\n\t\t{\n\t\t\t$this->db->data_cache['db_names'][] = $db_name;\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Drop database\n\t *\n\t * @param\tstring\t$db_name\n\t * @return\tbool\n\t */\n\tpublic function drop_database($db_name)\n\t{\n\t\tif ($this->_drop_database === FALSE)\n\t\t{\n\t\t\treturn ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;\n\t\t}\n\t\telseif ( ! $this->db->query(sprintf($this->_drop_database, $this->db->escape_identifiers($db_name))))\n\t\t{\n\t\t\treturn ($this->db->db_debug) ? $this->db->display_error('db_unable_to_drop') : FALSE;\n\t\t}\n\n\t\tif ( ! empty($this->db->data_cache['db_names']))\n\t\t{\n\t\t\t$key = array_search(strtolower($db_name), array_map('strtolower', $this->db->data_cache['db_names']), TRUE);\n\t\t\tif ($key !== FALSE)\n\t\t\t{\n\t\t\t\tunset($this->db->data_cache['db_names'][$key]);\n\t\t\t}\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Add Key\n\t *\n\t * @param\tstring\t$key\n\t * @param\tbool\t$primary\n\t * @return\tCI_DB_forge\n\t */\n\tpublic function add_key($key, $primary = FALSE)\n\t{\n\t\t// DO NOT change this! This condition is only applicable\n\t\t// for PRIMARY keys because you can only have one such,\n\t\t// and therefore all fields you add to it will be included\n\t\t// in the same, composite PRIMARY KEY.\n\t\t//\n\t\t// It's not the same for regular indexes.\n\t\tif ($primary === TRUE && is_array($key))\n\t\t{\n\t\t\tforeach ($key as $one)\n\t\t\t{\n\t\t\t\t$this->add_key($one, $primary);\n\t\t\t}\n\n\t\t\treturn $this;\n\t\t}\n\n\t\tif ($primary === TRUE)\n\t\t{\n\t\t\t$this->primary_keys[] = $key;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$this->keys[] = $key;\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Add Field\n\t *\n\t * @param\tarray\t$field\n\t * @return\tCI_DB_forge\n\t */\n\tpublic function add_field($field)\n\t{\n\t\tif (is_string($field))\n\t\t{\n\t\t\tif ($field === 'id')\n\t\t\t{\n\t\t\t\t$this->add_field(array(\n\t\t\t\t\t'id' => array(\n\t\t\t\t\t\t'type' => 'INT',\n\t\t\t\t\t\t'constraint' => 9,\n\t\t\t\t\t\t'auto_increment' => TRUE\n\t\t\t\t\t)\n\t\t\t\t));\n\t\t\t\t$this->add_key('id', TRUE);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (strpos($field, ' ') === FALSE)\n\t\t\t\t{\n\t\t\t\t\tshow_error('Field information is required for that operation.');\n\t\t\t\t}\n\n\t\t\t\t$this->fields[] = $field;\n\t\t\t}\n\t\t}\n\n\t\tif (is_array($field))\n\t\t{\n\t\t\t$this->fields = array_merge($this->fields, $field);\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Create Table\n\t *\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tbool\t$if_not_exists\tWhether to add IF NOT EXISTS condition\n\t * @param\tarray\t$attributes\tAssociative array of table attributes\n\t * @return\tbool\n\t */\n\tpublic function create_table($table, $if_not_exists = FALSE, array $attributes = array())\n\t{\n\t\tif ($table === '')\n\t\t{\n\t\t\tshow_error('A table name is required for that operation.');\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$table = $this->db->dbprefix.$table;\n\t\t}\n\n\t\tif (count($this->fields) === 0)\n\t\t{\n\t\t\tshow_error('Field information is required.');\n\t\t}\n\n\t\t$sql = $this->_create_table($table, $if_not_exists, $attributes);\n\n\t\tif (is_bool($sql))\n\t\t{\n\t\t\t$this->_reset();\n\t\t\tif ($sql === FALSE)\n\t\t\t{\n\t\t\t\treturn ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;\n\t\t\t}\n\t\t}\n\n\t\tif (($result = $this->db->query($sql)) !== FALSE)\n\t\t{\n\t\t\tif (isset($this->db->data_cache['table_names']))\n\t\t\t{\n\t\t\t\t$this->db->data_cache['table_names'][] = $table;\n\t\t\t}\n\n\t\t\t// Most databases don't support creating indexes from within the CREATE TABLE statement\n\t\t\tif ( ! empty($this->keys))\n\t\t\t{\n\t\t\t\tfor ($i = 0, $sqls = $this->_process_indexes($table), $c = count($sqls); $i < $c; $i++)\n\t\t\t\t{\n\t\t\t\t\t$this->db->query($sqls[$i]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$this->_reset();\n\t\treturn $result;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Create Table\n\t *\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tbool\t$if_not_exists\tWhether to add 'IF NOT EXISTS' condition\n\t * @param\tarray\t$attributes\tAssociative array of table attributes\n\t * @return\tmixed\n\t */\n\tprotected function _create_table($table, $if_not_exists, $attributes)\n\t{\n\t\tif ($if_not_exists === TRUE && $this->_create_table_if === FALSE)\n\t\t{\n\t\t\tif ($this->db->table_exists($table))\n\t\t\t{\n\t\t\t\treturn TRUE;\n\t\t\t}\n\n\t\t\t$if_not_exists = FALSE;\n\t\t}\n\n\t\t$sql = ($if_not_exists)\n\t\t\t? sprintf($this->_create_table_if, $this->db->escape_identifiers($table))\n\t\t\t: 'CREATE TABLE';\n\n\t\t$columns = $this->_process_fields(TRUE);\n\t\tfor ($i = 0, $c = count($columns); $i < $c; $i++)\n\t\t{\n\t\t\t$columns[$i] = ($columns[$i]['_literal'] !== FALSE)\n\t\t\t\t\t? \"\\n\\t\".$columns[$i]['_literal']\n\t\t\t\t\t: \"\\n\\t\".$this->_process_column($columns[$i]);\n\t\t}\n\n\t\t$columns = implode(',', $columns)\n\t\t\t\t.$this->_process_primary_keys($table);\n\n\t\t// Are indexes created from within the CREATE TABLE statement? (e.g. in MySQL)\n\t\tif ($this->_create_table_keys === TRUE)\n\t\t{\n\t\t\t$columns .= $this->_process_indexes($table);\n\t\t}\n\n\t\t// _create_table will usually have the following format: \"%s %s (%s\\n)\"\n\t\t$sql = sprintf($this->_create_table.'%s',\n\t\t\t$sql,\n\t\t\t$this->db->escape_identifiers($table),\n\t\t\t$columns,\n\t\t\t$this->_create_table_attr($attributes)\n\t\t);\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * CREATE TABLE attributes\n\t *\n\t * @param\tarray\t$attributes\tAssociative array of table attributes\n\t * @return\tstring\n\t */\n\tprotected function _create_table_attr($attributes)\n\t{\n\t\t$sql = '';\n\n\t\tforeach (array_keys($attributes) as $key)\n\t\t{\n\t\t\tif (is_string($key))\n\t\t\t{\n\t\t\t\t$sql .= ' '.strtoupper($key).' '.$attributes[$key];\n\t\t\t}\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Drop Table\n\t *\n\t * @param\tstring\t$table_name\tTable name\n\t * @param\tbool\t$if_exists\tWhether to add an IF EXISTS condition\n\t * @return\tbool\n\t */\n\tpublic function drop_table($table_name, $if_exists = FALSE)\n\t{\n\t\tif ($table_name === '')\n\t\t{\n\t\t\treturn ($this->db->db_debug) ? $this->db->display_error('db_table_name_required') : FALSE;\n\t\t}\n\n\t\tif (($query = $this->_drop_table($this->db->dbprefix.$table_name, $if_exists)) === TRUE)\n\t\t{\n\t\t\treturn TRUE;\n\t\t}\n\n\t\t$query = $this->db->query($query);\n\n\t\t// Update table list cache\n\t\tif ($query && ! empty($this->db->data_cache['table_names']))\n\t\t{\n\t\t\t$key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE);\n\t\t\tif ($key !== FALSE)\n\t\t\t{\n\t\t\t\tunset($this->db->data_cache['table_names'][$key]);\n\t\t\t}\n\t\t}\n\n\t\treturn $query;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Drop Table\n\t *\n\t * Generates a platform-specific DROP TABLE string\n\t *\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tbool\t$if_exists\tWhether to add an IF EXISTS condition\n\t * @return\tmixed\t(Returns a platform-specific DROP table string, or TRUE to indicate there's nothing to do)\n\t */\n\tprotected function _drop_table($table, $if_exists)\n\t{\n\t\t$sql = 'DROP TABLE';\n\n\t\tif ($if_exists)\n\t\t{\n\t\t\tif ($this->_drop_table_if === FALSE)\n\t\t\t{\n\t\t\t\tif ( ! $this->db->table_exists($table))\n\t\t\t\t{\n\t\t\t\t\treturn TRUE;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$sql = sprintf($this->_drop_table_if, $this->db->escape_identifiers($table));\n\t\t\t}\n\t\t}\n\n\t\treturn $sql.' '.$this->db->escape_identifiers($table);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Rename Table\n\t *\n\t * @param\tstring\t$table_name\tOld table name\n\t * @param\tstring\t$new_table_name\tNew table name\n\t * @return\tbool\n\t */\n\tpublic function rename_table($table_name, $new_table_name)\n\t{\n\t\tif ($table_name === '' OR $new_table_name === '')\n\t\t{\n\t\t\tshow_error('A table name is required for that operation.');\n\t\t\treturn FALSE;\n\t\t}\n\t\telseif ($this->_rename_table === FALSE)\n\t\t{\n\t\t\treturn ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;\n\t\t}\n\n\t\t$result = $this->db->query(sprintf($this->_rename_table,\n\t\t\t\t\t\t$this->db->escape_identifiers($this->db->dbprefix.$table_name),\n\t\t\t\t\t\t$this->db->escape_identifiers($this->db->dbprefix.$new_table_name))\n\t\t\t\t\t);\n\n\t\tif ($result && ! empty($this->db->data_cache['table_names']))\n\t\t{\n\t\t\t$key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE);\n\t\t\tif ($key !== FALSE)\n\t\t\t{\n\t\t\t\t$this->db->data_cache['table_names'][$key] = $this->db->dbprefix.$new_table_name;\n\t\t\t}\n\t\t}\n\n\t\treturn $result;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Column Add\n\t *\n\t * @param\tstring\t$table\tTable name\n\t * @param\tarray\t$field\tColumn definition\n\t * @return\tbool\n\t */\n\tpublic function add_column($table, $field)\n\t{\n\t\t// Work-around for literal column definitions\n\t\tis_array($field) OR $field = array($field);\n\n\t\tforeach (array_keys($field) as $k)\n\t\t{\n\t\t\t$this->add_field(array($k => $field[$k]));\n\t\t}\n\n\t\t$sqls = $this->_alter_table('ADD', $this->db->dbprefix.$table, $this->_process_fields());\n\t\t$this->_reset();\n\t\tif ($sqls === FALSE)\n\t\t{\n\t\t\treturn ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;\n\t\t}\n\n\t\tfor ($i = 0, $c = count($sqls); $i < $c; $i++)\n\t\t{\n\t\t\tif ($this->db->query($sqls[$i]) === FALSE)\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Column Drop\n\t *\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tstring\t$column_name\tColumn name\n\t * @return\tbool\n\t */\n\tpublic function drop_column($table, $column_name)\n\t{\n\t\t$sql = $this->_alter_table('DROP', $this->db->dbprefix.$table, $column_name);\n\t\tif ($sql === FALSE)\n\t\t{\n\t\t\treturn ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;\n\t\t}\n\n\t\treturn $this->db->query($sql);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Column Modify\n\t *\n\t * @param\tstring\t$table\tTable name\n\t * @param\tstring\t$field\tColumn definition\n\t * @return\tbool\n\t */\n\tpublic function modify_column($table, $field)\n\t{\n\t\t// Work-around for literal column definitions\n\t\tis_array($field) OR $field = array($field);\n\n\t\tforeach (array_keys($field) as $k)\n\t\t{\n\t\t\t$this->add_field(array($k => $field[$k]));\n\t\t}\n\n\t\tif (count($this->fields) === 0)\n\t\t{\n\t\t\tshow_error('Field information is required.');\n\t\t}\n\n\t\t$sqls = $this->_alter_table('CHANGE', $this->db->dbprefix.$table, $this->_process_fields());\n\t\t$this->_reset();\n\t\tif ($sqls === FALSE)\n\t\t{\n\t\t\treturn ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;\n\t\t}\n\n\t\tfor ($i = 0, $c = count($sqls); $i < $c; $i++)\n\t\t{\n\t\t\tif ($this->db->query($sqls[$i]) === FALSE)\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ALTER TABLE\n\t *\n\t * @param\tstring\t$alter_type\tALTER type\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tmixed\t$field\t\tColumn definition\n\t * @return\tstring|string[]\n\t */\n\tprotected function _alter_table($alter_type, $table, $field)\n\t{\n\t\t$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' ';\n\n\t\t// DROP has everything it needs now.\n\t\tif ($alter_type === 'DROP')\n\t\t{\n\t\t\treturn $sql.'DROP COLUMN '.$this->db->escape_identifiers($field);\n\t\t}\n\n\t\t$sql .= ($alter_type === 'ADD')\n\t\t\t? 'ADD '\n\t\t\t: $alter_type.' COLUMN ';\n\n\t\t$sqls = array();\n\t\tfor ($i = 0, $c = count($field); $i < $c; $i++)\n\t\t{\n\t\t\t$sqls[] = $sql\n\t\t\t\t.($field[$i]['_literal'] !== FALSE ? $field[$i]['_literal'] : $this->_process_column($field[$i]));\n\t\t}\n\n\t\treturn $sqls;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Process fields\n\t *\n\t * @param\tbool\t$create_table\n\t * @return\tarray\n\t */\n\tprotected function _process_fields($create_table = FALSE)\n\t{\n\t\t$fields = array();\n\n\t\tforeach ($this->fields as $key => $attributes)\n\t\t{\n\t\t\tif (is_int($key) && ! is_array($attributes))\n\t\t\t{\n\t\t\t\t$fields[] = array('_literal' => $attributes);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$attributes = array_change_key_case($attributes, CASE_UPPER);\n\n\t\t\tif ($create_table === TRUE && empty($attributes['TYPE']))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tisset($attributes['TYPE']) && $this->_attr_type($attributes);\n\n\t\t\t$field = array(\n\t\t\t\t'name'\t\t\t=> $key,\n\t\t\t\t'new_name'\t\t=> isset($attributes['NAME']) ? $attributes['NAME'] : NULL,\n\t\t\t\t'type'\t\t\t=> isset($attributes['TYPE']) ? $attributes['TYPE'] : NULL,\n\t\t\t\t'length'\t\t=> '',\n\t\t\t\t'unsigned'\t\t=> '',\n\t\t\t\t'null'\t\t\t=> NULL,\n\t\t\t\t'unique'\t\t=> '',\n\t\t\t\t'default'\t\t=> '',\n\t\t\t\t'auto_increment'\t=> '',\n\t\t\t\t'_literal'\t\t=> FALSE\n\t\t\t);\n\n\t\t\tisset($attributes['TYPE']) && $this->_attr_unsigned($attributes, $field);\n\n\t\t\tif ($create_table === FALSE)\n\t\t\t{\n\t\t\t\tif (isset($attributes['AFTER']))\n\t\t\t\t{\n\t\t\t\t\t$field['after'] = $attributes['AFTER'];\n\t\t\t\t}\n\t\t\t\telseif (isset($attributes['FIRST']))\n\t\t\t\t{\n\t\t\t\t\t$field['first'] = (bool) $attributes['FIRST'];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$this->_attr_default($attributes, $field);\n\n\t\t\tif (isset($attributes['NULL']))\n\t\t\t{\n\t\t\t\tif ($attributes['NULL'] === TRUE)\n\t\t\t\t{\n\t\t\t\t\t$field['null'] = empty($this->_null) ? '' : ' '.$this->_null;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$field['null'] = ' NOT NULL';\n\t\t\t\t}\n\t\t\t}\n\t\t\telseif ($create_table === TRUE)\n\t\t\t{\n\t\t\t\t$field['null'] = ' NOT NULL';\n\t\t\t}\n\n\t\t\t$this->_attr_auto_increment($attributes, $field);\n\t\t\t$this->_attr_unique($attributes, $field);\n\n\t\t\tif (isset($attributes['COMMENT']))\n\t\t\t{\n\t\t\t\t$field['comment'] = $this->db->escape($attributes['COMMENT']);\n\t\t\t}\n\n\t\t\tif (isset($attributes['TYPE']) && ! empty($attributes['CONSTRAINT']))\n\t\t\t{\n\t\t\t\tswitch (strtoupper($attributes['TYPE']))\n\t\t\t\t{\n\t\t\t\t\tcase 'ENUM':\n\t\t\t\t\tcase 'SET':\n\t\t\t\t\t\t$attributes['CONSTRAINT'] = $this->db->escape($attributes['CONSTRAINT']);\n\t\t\t\t\tdefault:\n\t\t\t\t\t\t$field['length'] = is_array($attributes['CONSTRAINT'])\n\t\t\t\t\t\t\t? '('.implode(',', $attributes['CONSTRAINT']).')'\n\t\t\t\t\t\t\t: '('.$attributes['CONSTRAINT'].')';\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$fields[] = $field;\n\t\t}\n\n\t\treturn $fields;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Process column\n\t *\n\t * @param\tarray\t$field\n\t * @return\tstring\n\t */\n\tprotected function _process_column($field)\n\t{\n\t\treturn $this->db->escape_identifiers($field['name'])\n\t\t\t.' '.$field['type'].$field['length']\n\t\t\t.$field['unsigned']\n\t\t\t.$field['default']\n\t\t\t.$field['null']\n\t\t\t.$field['auto_increment']\n\t\t\t.$field['unique'];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute TYPE\n\t *\n\t * Performs a data type mapping between different databases.\n\t *\n\t * @param\tarray\t&$attributes\n\t * @return\tvoid\n\t */\n\tprotected function _attr_type(&$attributes)\n\t{\n\t\t// Usually overridden by drivers\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute UNSIGNED\n\t *\n\t * Depending on the _unsigned property value:\n\t *\n\t *\t- TRUE will always set $field['unsigned'] to 'UNSIGNED'\n\t *\t- FALSE will always set $field['unsigned'] to ''\n\t *\t- array(TYPE) will set $field['unsigned'] to 'UNSIGNED',\n\t *\t\tif $attributes['TYPE'] is found in the array\n\t *\t- array(TYPE => UTYPE) will change $field['type'],\n\t *\t\tfrom TYPE to UTYPE in case of a match\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_unsigned(&$attributes, &$field)\n\t{\n\t\tif (empty($attributes['UNSIGNED']) OR $attributes['UNSIGNED'] !== TRUE)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t// Reset the attribute in order to avoid issues if we do type conversion\n\t\t$attributes['UNSIGNED'] = FALSE;\n\n\t\tif (is_array($this->_unsigned))\n\t\t{\n\t\t\tforeach (array_keys($this->_unsigned) as $key)\n\t\t\t{\n\t\t\t\tif (is_int($key) && strcasecmp($attributes['TYPE'], $this->_unsigned[$key]) === 0)\n\t\t\t\t{\n\t\t\t\t\t$field['unsigned'] = ' UNSIGNED';\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\telseif (is_string($key) && strcasecmp($attributes['TYPE'], $key) === 0)\n\t\t\t\t{\n\t\t\t\t\t$field['type'] = $key;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\t$field['unsigned'] = ($this->_unsigned === TRUE) ? ' UNSIGNED' : '';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute DEFAULT\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_default(&$attributes, &$field)\n\t{\n\t\tif ($this->_default === FALSE)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tif ( ! array_key_exists('DEFAULT', $attributes))\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tif ($attributes['DEFAULT'] === NULL)\n\t\t{\n\t\t\t$field['default'] = empty($this->_null) ? '' : $this->_default.$this->_null;\n\n\t\t\t// Override the NULL attribute if that's our default\n\t\t\t$attributes['NULL'] = TRUE;\n\t\t\t$field['null'] = empty($this->_null) ? '' : ' '.$this->_null;\n\t\t\treturn;\n\t\t}\n\n\t\t// White-list CURRENT_TIMESTAMP & similar (e.g. Oracle has stuff like SYSTIMESTAMP) defaults for date/time fields\n\t\tif (\n\t\t\tisset($attributes['TYPE'])\n\t\t\t&& (stripos($attributes['TYPE'],    'time') !== FALSE OR stripos($attributes['TYPE'],    'date') !== FALSE)\n\t\t\t&& (stripos($attributes['DEFAULT'], 'time') !== FALSE OR stripos($attributes['DEFAULT'], 'date') !== FALSE)\n\t\t)\n\t\t{\n\t\t\t$field['default'] = $this->_default.$attributes['DEFAULT'];\n\t\t\treturn;\n\t\t}\n\n\t\t$field['default'] = $this->_default.$this->db->escape($attributes['DEFAULT']);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute UNIQUE\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_unique(&$attributes, &$field)\n\t{\n\t\tif ( ! empty($attributes['UNIQUE']) && $attributes['UNIQUE'] === TRUE)\n\t\t{\n\t\t\t$field['unique'] = ' UNIQUE';\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute AUTO_INCREMENT\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_auto_increment(&$attributes, &$field)\n\t{\n\t\tif ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE)\n\t\t{\n\t\t\t$field['auto_increment'] = ' AUTO_INCREMENT';\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Process primary keys\n\t *\n\t * @param\tstring\t$table\tTable name\n\t * @return\tstring\n\t */\n\tprotected function _process_primary_keys($table)\n\t{\n\t\t$sql = '';\n\n\t\tfor ($i = 0, $c = count($this->primary_keys); $i < $c; $i++)\n\t\t{\n\t\t\tif ( ! isset($this->fields[$this->primary_keys[$i]]))\n\t\t\t{\n\t\t\t\tunset($this->primary_keys[$i]);\n\t\t\t}\n\t\t}\n\n\t\tif (count($this->primary_keys) > 0)\n\t\t{\n\t\t\t$sql .= \",\\n\\tCONSTRAINT \".$this->db->escape_identifiers('pk_'.$table)\n\t\t\t\t.' PRIMARY KEY('.implode(', ', $this->db->escape_identifiers($this->primary_keys)).')';\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Process indexes\n\t *\n\t * @param\tstring\t$table\tTable name\n\t * @return\tstring[] list of SQL statements\n\t */\n\tprotected function _process_indexes($table)\n\t{\n\t\t$sqls = array();\n\n\t\tfor ($i = 0, $c = count($this->keys); $i < $c; $i++)\n\t\t{\n\t\t\tif (is_array($this->keys[$i]))\n\t\t\t{\n\t\t\t\tfor ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++)\n\t\t\t\t{\n\t\t\t\t\tif ( ! isset($this->fields[$this->keys[$i][$i2]]))\n\t\t\t\t\t{\n\t\t\t\t\t\tunset($this->keys[$i][$i2]);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telseif ( ! isset($this->fields[$this->keys[$i]]))\n\t\t\t{\n\t\t\t\tunset($this->keys[$i]);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tis_array($this->keys[$i]) OR $this->keys[$i] = array($this->keys[$i]);\n\n\t\t\t$sqls[] = 'CREATE INDEX '.$this->db->escape_identifiers($table.'_'.implode('_', $this->keys[$i]))\n\t\t\t\t.' ON '.$this->db->escape_identifiers($table)\n\t\t\t\t.' ('.implode(', ', $this->db->escape_identifiers($this->keys[$i])).');';\n\t\t}\n\n\t\treturn $sqls;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Reset\n\t *\n\t * Resets table creation vars\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _reset()\n\t{\n\t\t$this->fields = $this->keys = $this->primary_keys = array();\n\t}\n\n}\n"
  },
  {
    "path": "system/database/DB_query_builder.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Query Builder Class\n *\n * This is the platform-independent base Query Builder implementation class.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\n\nabstract class CI_DB_query_builder extends CI_DB_driver {\n\n\t/**\n\t * Return DELETE SQL flag\n\t *\n\t * @var\tbool\n\t */\n\tprotected $return_delete_sql\t\t= FALSE;\n\n\t/**\n\t * Reset DELETE data flag\n\t *\n\t * @var\tbool\n\t */\n\tprotected $reset_delete_data\t\t= FALSE;\n\n\t/**\n\t * QB SELECT data\n\t *\n\t * @var\tarray\n\t */\n\tprotected $qb_select\t\t\t= array();\n\n\t/**\n\t * QB DISTINCT flag\n\t *\n\t * @var\tbool\n\t */\n\tprotected $qb_distinct\t\t\t= FALSE;\n\n\t/**\n\t * QB FROM data\n\t *\n\t * @var\tarray\n\t */\n\tprotected $qb_from\t\t\t= array();\n\n\t/**\n\t * QB JOIN data\n\t *\n\t * @var\tarray\n\t */\n\tprotected $qb_join\t\t\t= array();\n\n\t/**\n\t * QB WHERE data\n\t *\n\t * @var\tarray\n\t */\n\tprotected $qb_where\t\t\t= array();\n\n\t/**\n\t * QB GROUP BY data\n\t *\n\t * @var\tarray\n\t */\n\tprotected $qb_groupby\t\t\t= array();\n\n\t/**\n\t * QB HAVING data\n\t *\n\t * @var\tarray\n\t */\n\tprotected $qb_having\t\t\t= array();\n\n\t/**\n\t * QB keys\n\t *\n\t * @var\tarray\n\t */\n\tprotected $qb_keys\t\t\t= array();\n\n\t/**\n\t * QB LIMIT data\n\t *\n\t * @var\tint\n\t */\n\tprotected $qb_limit\t\t\t= FALSE;\n\n\t/**\n\t * QB OFFSET data\n\t *\n\t * @var\tint\n\t */\n\tprotected $qb_offset\t\t\t= FALSE;\n\n\t/**\n\t * QB ORDER BY data\n\t *\n\t * @var\tarray\n\t */\n\tprotected $qb_orderby\t\t\t= array();\n\n\t/**\n\t * QB data sets\n\t *\n\t * @var\tarray\n\t */\n\tprotected $qb_set\t\t\t= array();\n\n\t/**\n\t * QB data set for update_batch()\n\t *\n\t * @var\tarray\n\t */\n\tprotected $qb_set_ub\t\t\t= array();\n\n\t/**\n\t * QB aliased tables list\n\t *\n\t * @var\tarray\n\t */\n\tprotected $qb_aliased_tables\t\t= array();\n\n\t/**\n\t * QB WHERE group started flag\n\t *\n\t * @var\tbool\n\t */\n\tprotected $qb_where_group_started\t= FALSE;\n\n\t/**\n\t * QB WHERE group count\n\t *\n\t * @var\tint\n\t */\n\tprotected $qb_where_group_count\t\t= 0;\n\n\t// Query Builder Caching variables\n\n\t/**\n\t * QB Caching flag\n\t *\n\t * @var\tbool\n\t */\n\tprotected $qb_caching\t\t\t\t= FALSE;\n\n\t/**\n\t * QB Cache exists list\n\t *\n\t * @var\tarray\n\t */\n\tprotected $qb_cache_exists\t\t\t= array();\n\n\t/**\n\t * QB Cache SELECT data\n\t *\n\t * @var\tarray\n\t */\n\tprotected $qb_cache_select\t\t\t= array();\n\n\t/**\n\t * QB Cache FROM data\n\t *\n\t * @var\tarray\n\t */\n\tprotected $qb_cache_from\t\t\t= array();\n\n\t/**\n\t * QB Cache JOIN data\n\t *\n\t * @var\tarray\n\t */\n\tprotected $qb_cache_join\t\t\t= array();\n\n\t/**\n\t * QB Cache aliased tables list\n\t *\n\t * @var\tarray\n\t */\n\tprotected $qb_cache_aliased_tables\t\t\t= array();\n\n\t/**\n\t * QB Cache WHERE data\n\t *\n\t * @var\tarray\n\t */\n\tprotected $qb_cache_where\t\t\t= array();\n\n\t/**\n\t * QB Cache GROUP BY data\n\t *\n\t * @var\tarray\n\t */\n\tprotected $qb_cache_groupby\t\t\t= array();\n\n\t/**\n\t * QB Cache HAVING data\n\t *\n\t * @var\tarray\n\t */\n\tprotected $qb_cache_having\t\t\t= array();\n\n\t/**\n\t * QB Cache ORDER BY data\n\t *\n\t * @var\tarray\n\t */\n\tprotected $qb_cache_orderby\t\t\t= array();\n\n\t/**\n\t * QB Cache data sets\n\t *\n\t * @var\tarray\n\t */\n\tprotected $qb_cache_set\t\t\t\t= array();\n\n\t/**\n\t * QB No Escape data\n\t *\n\t * @var\tarray\n\t */\n\tprotected $qb_no_escape \t\t\t= array();\n\n\t/**\n\t * QB Cache No Escape data\n\t *\n\t * @var\tarray\n\t */\n\tprotected $qb_cache_no_escape\t\t\t= array();\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Select\n\t *\n\t * Generates the SELECT portion of the query\n\t *\n\t * @param\tstring\n\t * @param\tmixed\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function select($select = '*', $escape = NULL)\n\t{\n\t\tif (is_string($select))\n\t\t{\n\t\t\t$select = explode(',', $select);\n\t\t}\n\n\t\t// If the escape value was not set, we will base it on the global setting\n\t\tis_bool($escape) OR $escape = $this->_protect_identifiers;\n\n\t\tforeach ($select as $val)\n\t\t{\n\t\t\t$val = trim($val);\n\n\t\t\tif ($val !== '')\n\t\t\t{\n\t\t\t\t$this->qb_select[] = $val;\n\t\t\t\t$this->qb_no_escape[] = $escape;\n\n\t\t\t\tif ($this->qb_caching === TRUE)\n\t\t\t\t{\n\t\t\t\t\t$this->qb_cache_select[] = $val;\n\t\t\t\t\t$this->qb_cache_exists[] = 'select';\n\t\t\t\t\t$this->qb_cache_no_escape[] = $escape;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Select Max\n\t *\n\t * Generates a SELECT MAX(field) portion of a query\n\t *\n\t * @param\tstring\tthe field\n\t * @param\tstring\tan alias\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function select_max($select = '', $alias = '')\n\t{\n\t\treturn $this->_max_min_avg_sum($select, $alias, 'MAX');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Select Min\n\t *\n\t * Generates a SELECT MIN(field) portion of a query\n\t *\n\t * @param\tstring\tthe field\n\t * @param\tstring\tan alias\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function select_min($select = '', $alias = '')\n\t{\n\t\treturn $this->_max_min_avg_sum($select, $alias, 'MIN');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Select Average\n\t *\n\t * Generates a SELECT AVG(field) portion of a query\n\t *\n\t * @param\tstring\tthe field\n\t * @param\tstring\tan alias\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function select_avg($select = '', $alias = '')\n\t{\n\t\treturn $this->_max_min_avg_sum($select, $alias, 'AVG');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Select Sum\n\t *\n\t * Generates a SELECT SUM(field) portion of a query\n\t *\n\t * @param\tstring\tthe field\n\t * @param\tstring\tan alias\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function select_sum($select = '', $alias = '')\n\t{\n\t\treturn $this->_max_min_avg_sum($select, $alias, 'SUM');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * SELECT [MAX|MIN|AVG|SUM]()\n\t *\n\t * @used-by\tselect_max()\n\t * @used-by\tselect_min()\n\t * @used-by\tselect_avg()\n\t * @used-by\tselect_sum()\n\t *\n\t * @param\tstring\t$select\tField name\n\t * @param\tstring\t$alias\n\t * @param\tstring\t$type\n\t * @return\tCI_DB_query_builder\n\t */\n\tprotected function _max_min_avg_sum($select = '', $alias = '', $type = 'MAX')\n\t{\n\t\tif ( ! is_string($select) OR $select === '')\n\t\t{\n\t\t\t$this->display_error('db_invalid_query');\n\t\t}\n\n\t\t$type = strtoupper($type);\n\n\t\tif ( ! in_array($type, array('MAX', 'MIN', 'AVG', 'SUM')))\n\t\t{\n\t\t\tshow_error('Invalid function type: '.$type);\n\t\t}\n\n\t\tif ($alias === '')\n\t\t{\n\t\t\t$alias = $this->_create_alias_from_table(trim($select));\n\t\t}\n\n\t\t$sql = $type.'('.$this->protect_identifiers(trim($select)).') AS '.$this->escape_identifiers(trim($alias));\n\n\t\t$this->qb_select[] = $sql;\n\t\t$this->qb_no_escape[] = NULL;\n\n\t\tif ($this->qb_caching === TRUE)\n\t\t{\n\t\t\t$this->qb_cache_select[] = $sql;\n\t\t\t$this->qb_cache_exists[] = 'select';\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Determines the alias name based on the table\n\t *\n\t * @param\tstring\t$item\n\t * @return\tstring\n\t */\n\tprotected function _create_alias_from_table($item)\n\t{\n\t\tif (strpos($item, '.') !== FALSE)\n\t\t{\n\t\t\t$item = explode('.', $item);\n\t\t\treturn end($item);\n\t\t}\n\n\t\treturn $item;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * DISTINCT\n\t *\n\t * Sets a flag which tells the query string compiler to add DISTINCT\n\t *\n\t * @param\tbool\t$val\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function distinct($val = TRUE)\n\t{\n\t\t$this->qb_distinct = is_bool($val) ? $val : TRUE;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * From\n\t *\n\t * Generates the FROM portion of the query\n\t *\n\t * @param\tmixed\t$from\tcan be a string or array\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function from($from)\n\t{\n\t\tforeach ((array) $from as $val)\n\t\t{\n\t\t\tif (strpos($val, ',') !== FALSE)\n\t\t\t{\n\t\t\t\tforeach (explode(',', $val) as $v)\n\t\t\t\t{\n\t\t\t\t\t$v = trim($v);\n\t\t\t\t\t$this->_track_aliases($v);\n\n\t\t\t\t\t$this->qb_from[] = $v = $this->protect_identifiers($v, TRUE, NULL, FALSE);\n\n\t\t\t\t\tif ($this->qb_caching === TRUE)\n\t\t\t\t\t{\n\t\t\t\t\t\t$this->qb_cache_from[] = $v;\n\t\t\t\t\t\t$this->qb_cache_exists[] = 'from';\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$val = trim($val);\n\n\t\t\t\t// Extract any aliases that might exist. We use this information\n\t\t\t\t// in the protect_identifiers to know whether to add a table prefix\n\t\t\t\t$this->_track_aliases($val);\n\n\t\t\t\t$this->qb_from[] = $val = $this->protect_identifiers($val, TRUE, NULL, FALSE);\n\n\t\t\t\tif ($this->qb_caching === TRUE)\n\t\t\t\t{\n\t\t\t\t\t$this->qb_cache_from[] = $val;\n\t\t\t\t\t$this->qb_cache_exists[] = 'from';\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * JOIN\n\t *\n\t * Generates the JOIN portion of the query\n\t *\n\t * @param\tstring\n\t * @param\tstring\tthe join condition\n\t * @param\tstring\tthe type of join\n\t * @param\tstring\twhether not to try to escape identifiers\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function join($table, $cond, $type = '', $escape = NULL)\n\t{\n\t\t$type = trim(strtoupper($type).' JOIN');\n\t\tpreg_match('#^(NATURAL\\s+)?((LEFT|RIGHT|FULL)\\s+)?((INNER|OUTER)\\s+)?JOIN$#', $type) OR $type = 'JOIN';\n\n\t\t// Extract any aliases that might exist. We use this information\n\t\t// in the protect_identifiers to know whether to add a table prefix\n\t\t$this->_track_aliases($table);\n\n\t\tis_bool($escape) OR $escape = $this->_protect_identifiers;\n\n\t\tif (strpos($type, 'NATURAL') === 0)\n\t\t{\n\t\t\t$cond = '';\n\t\t}\n\t\telseif ( ! $this->_has_operator($cond))\n\t\t{\n\t\t\t$cond = ' USING ('.($escape ? $this->escape_identifiers($cond) : $cond).')';\n\t\t}\n\t\telseif ($escape === FALSE)\n\t\t{\n\t\t\t$cond = ' ON '.$cond;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Split multiple conditions\n\t\t\tif (preg_match_all('/\\sAND\\s|\\sOR\\s/i', $cond, $joints, PREG_OFFSET_CAPTURE))\n\t\t\t{\n\t\t\t\t$conditions = array();\n\t\t\t\t$joints = $joints[0];\n\t\t\t\tarray_unshift($joints, array('', 0));\n\n\t\t\t\tfor ($i = count($joints) - 1, $pos = strlen($cond); $i >= 0; $i--)\n\t\t\t\t{\n\t\t\t\t\t$joints[$i][1] += strlen($joints[$i][0]); // offset\n\t\t\t\t\t$conditions[$i] = substr($cond, $joints[$i][1], $pos - $joints[$i][1]);\n\t\t\t\t\t$pos = $joints[$i][1] - strlen($joints[$i][0]);\n\t\t\t\t\t$joints[$i] = $joints[$i][0];\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$conditions = array($cond);\n\t\t\t\t$joints = array('');\n\t\t\t}\n\n\t\t\t$cond = ' ON ';\n\t\t\tfor ($i = 0, $c = count($conditions); $i < $c; $i++)\n\t\t\t{\n\t\t\t\t$operator = $this->_get_operator($conditions[$i]);\n\t\t\t\t$cond .= $joints[$i];\n\t\t\t\t$cond .= preg_match(\"/(\\(*)?([\\[\\]\\w\\.'-]+)\".preg_quote($operator).\"(.*)/i\", $conditions[$i], $match)\n\t\t\t\t\t? $match[1].$this->protect_identifiers($match[2]).$operator.$this->protect_identifiers($match[3])\n\t\t\t\t\t: $conditions[$i];\n\t\t\t}\n\t\t}\n\n\t\t// Do we want to escape the table name?\n\t\tif ($escape === TRUE)\n\t\t{\n\t\t\t$table = $this->protect_identifiers($table, TRUE, NULL, FALSE);\n\t\t}\n\n\t\t// Assemble the JOIN statement\n\t\t$this->qb_join[] = $join = $type.' '.$table.$cond;\n\n\t\tif ($this->qb_caching === TRUE)\n\t\t{\n\t\t\t$this->qb_cache_join[] = $join;\n\t\t\t$this->qb_cache_exists[] = 'join';\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * WHERE\n\t *\n\t * Generates the WHERE portion of the query.\n\t * Separates multiple calls with 'AND'.\n\t *\n\t * @param\tmixed\n\t * @param\tmixed\n\t * @param\tbool\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function where($key, $value = NULL, $escape = NULL)\n\t{\n\t\treturn $this->_wh('qb_where', $key, $value, 'AND ', $escape);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * OR WHERE\n\t *\n\t * Generates the WHERE portion of the query.\n\t * Separates multiple calls with 'OR'.\n\t *\n\t * @param\tmixed\n\t * @param\tmixed\n\t * @param\tbool\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function or_where($key, $value = NULL, $escape = NULL)\n\t{\n\t\treturn $this->_wh('qb_where', $key, $value, 'OR ', $escape);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * WHERE, HAVING\n\t *\n\t * @used-by\twhere()\n\t * @used-by\tor_where()\n\t * @used-by\thaving()\n\t * @used-by\tor_having()\n\t *\n\t * @param\tstring\t$qb_key\t'qb_where' or 'qb_having'\n\t * @param\tmixed\t$key\n\t * @param\tmixed\t$value\n\t * @param\tstring\t$type\n\t * @param\tbool\t$escape\n\t * @return\tCI_DB_query_builder\n\t */\n\tprotected function _wh($qb_key, $key, $value = NULL, $type = 'AND ', $escape = NULL)\n\t{\n\t\t$qb_cache_key = ($qb_key === 'qb_having') ? 'qb_cache_having' : 'qb_cache_where';\n\n\t\tif ( ! is_array($key))\n\t\t{\n\t\t\t$key = array($key => $value);\n\t\t}\n\n\t\t// If the escape value was not set will base it on the global setting\n\t\tis_bool($escape) OR $escape = $this->_protect_identifiers;\n\n\t\tforeach ($key as $k => $v)\n\t\t{\n\t\t\t$prefix = (count($this->$qb_key) === 0 && count($this->$qb_cache_key) === 0)\n\t\t\t\t? $this->_group_get_type('')\n\t\t\t\t: $this->_group_get_type($type);\n\n\t\t\tif ($v !== NULL)\n\t\t\t{\n\t\t\t\tif ($escape === TRUE)\n\t\t\t\t{\n\t\t\t\t\t$v = $this->escape($v);\n\t\t\t\t}\n\n\t\t\t\tif ( ! $this->_has_operator($k))\n\t\t\t\t{\n\t\t\t\t\t$k .= ' = ';\n\t\t\t\t}\n\t\t\t}\n\t\t\telseif ( ! $this->_has_operator($k))\n\t\t\t{\n\t\t\t\t// value appears not to have been set, assign the test to IS NULL\n\t\t\t\t$k .= ' IS NULL';\n\t\t\t}\n\t\t\telseif (preg_match('/\\s*(!?=|<>|\\sIS(?:\\s+NOT)?\\s)\\s*$/i', $k, $match, PREG_OFFSET_CAPTURE))\n\t\t\t{\n\t\t\t\t$k = substr($k, 0, $match[0][1]).($match[1][0] === '=' ? ' IS NULL' : ' IS NOT NULL');\n\t\t\t}\n\n\t\t\t$$qb_key = array('condition' => $prefix.$k, 'value' => $v, 'escape' => $escape);\n\t\t\t$this->{$qb_key}[] = $$qb_key;\n\t\t\tif ($this->qb_caching === TRUE)\n\t\t\t{\n\t\t\t\t$this->{$qb_cache_key}[] = $$qb_key;\n\t\t\t\t$this->qb_cache_exists[] = substr($qb_key, 3);\n\t\t\t}\n\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * WHERE IN\n\t *\n\t * Generates a WHERE field IN('item', 'item') SQL query,\n\t * joined with 'AND' if appropriate.\n\t *\n\t * @param\tstring\t$key\tThe field to search\n\t * @param\tarray\t$values\tThe values searched on\n\t * @param\tbool\t$escape\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function where_in($key, array $values, $escape = NULL)\n\t{\n\t\treturn $this->_wh_in('qb_where', $key, $values, FALSE, 'AND ', $escape);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * OR WHERE IN\n\t *\n\t * Generates a WHERE field IN('item', 'item') SQL query,\n\t * joined with 'OR' if appropriate.\n\t *\n\t * @param\tstring\t$key\tThe field to search\n\t * @param\tarray\t$values\tThe values searched on\n\t * @param\tbool\t$escape\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function or_where_in($key, array $values, $escape = NULL)\n\t{\n\t\treturn $this->_wh_in('qb_where', $key, $values, FALSE, 'OR ', $escape);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * WHERE NOT IN\n\t *\n\t * Generates a WHERE field NOT IN('item', 'item') SQL query,\n\t * joined with 'AND' if appropriate.\n\t *\n\t * @param\tstring\t$key\tThe field to search\n\t * @param\tarray\t$values\tThe values searched on\n\t * @param\tbool\t$escape\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function where_not_in($key, array $values, $escape = NULL)\n\t{\n\t\treturn $this->_wh_in('qb_where', $key, $values, TRUE, 'AND ', $escape);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * OR WHERE NOT IN\n\t *\n\t * Generates a WHERE field NOT IN('item', 'item') SQL query,\n\t * joined with 'OR' if appropriate.\n\t *\n\t * @param\tstring\t$key\tThe field to search\n\t * @param\tarray\t$values\tThe values searched on\n\t * @param\tbool\t$escape\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function or_where_not_in($key, array $values, $escape = NULL)\n\t{\n\t\treturn $this->_wh_in('qb_where', $key, $values, TRUE, 'OR ', $escape);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * HAVING IN\n\t *\n\t * Generates a HAVING field IN('item', 'item') SQL query,\n\t * joined with 'AND' if appropriate.\n\t *\n\t * @param\tstring\t$key\tThe field to search\n\t * @param\tarray\t$values\tThe values searched on\n\t * @param\tbool\t$escape\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function having_in($key, array $values, $escape = NULL)\n\t{\n\t\treturn $this->_wh_in('qb_having', $key, $values, FALSE, 'AND ', $escape);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * OR HAVING IN\n\t *\n\t * Generates a HAVING field IN('item', 'item') SQL query,\n\t * joined with 'OR' if appropriate.\n\t *\n\t * @param\tstring\t$key\tThe field to search\n\t * @param\tarray\t$values\tThe values searched on\n\t * @param\tbool\t$escape\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function or_having_in($key, array $values, $escape = NULL)\n\t{\n\t\treturn $this->_wh_in('qb_having', $key, $values, FALSE, 'OR ', $escape);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * HAVING NOT IN\n\t *\n\t * Generates a HAVING field NOT IN('item', 'item') SQL query,\n\t * joined with 'AND' if appropriate.\n\t *\n\t * @param\tstring\t$key\tThe field to search\n\t * @param\tarray\t$values\tThe values searched on\n\t * @param\tbool\t$escape\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function having_not_in($key, array $values, $escape = NULL)\n\t{\n\t\treturn $this->_wh_in('qb_having', $key, $values, TRUE, 'AND ', $escape);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * OR HAVING NOT IN\n\t *\n\t * Generates a HAVING field NOT IN('item', 'item') SQL query,\n\t * joined with 'OR' if appropriate.\n\t *\n\t * @param\tstring\t$key\tThe field to search\n\t * @param\tarray\t$values\tThe values searched on\n\t * @param\tbool\t$escape\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function or_having_not_in($key, array $values, $escape = NULL)\n\t{\n\t\treturn $this->_wh_in('qb_having', $key, $values, TRUE, 'OR ', $escape);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Internal WHERE/HAVING IN\n\t *\n\t * @used-by\twhere_in()\n\t * @used-by\tor_where_in()\n\t * @used-by\twhere_not_in()\n\t * @used-by\tor_where_not_in()\n\t * @used-by\thaving_in()\n\t * @used-by\tor_having_in()\n\t * @used-by\thaving_not_in()\n\t * @used-by\tor_having_not_in()\n\t *\n\t * @param\tstring\t$qb_key\t'qb_where' or 'qb_having'\n\t * @param\tstring\t$key\tThe field to search\n\t * @param\tarray\t$values\tThe values searched on\n\t * @param\tbool\t$not\tIf the statement would be IN or NOT IN\n\t * @param\tstring\t$type\n\t * @param\tbool\t$escape\n\t * @return\tCI_DB_query_builder\n\t */\n\tprotected function _wh_in($qb_key, $key, array $values, $not = FALSE, $type = 'AND ', $escape = NULL)\n\t{\n\t\t$qb_cache_key = ($qb_key === 'qb_having') ? 'qb_cache_having' : 'qb_cache_where';\n\n\t\tif (empty($key) OR ! is_string($key))\n\t\t{\n\t\t\tthrow new InvalidArgumentException(sprintf('%s() expects $key to be a non-empty string', debug_backtrace(0, 2)[1]['function']));\n\t\t}\n\n\t\tif (empty($values))\n\t\t{\n\t\t\tthrow new InvalidArgumentException(sprintf('%s() expects $values to be a non-empty array', debug_backtrace(0, 2)[1]['function']));\n\t\t}\n\n\t\tis_bool($escape) OR $escape = $this->_protect_identifiers;\n\n\t\t$not = ($not) ? ' NOT' : '';\n\n\t\tif ($escape === TRUE)\n\t\t{\n\t\t\t$wh_in = array();\n\t\t\tforeach ($values as $value)\n\t\t\t{\n\t\t\t\t$wh_in[] = $this->escape($value);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$wh_in = array_values($values);\n\t\t}\n\n\t\t$prefix = (count($this->$qb_key) === 0 && count($this->$qb_cache_key) === 0)\n\t\t\t? $this->_group_get_type('')\n\t\t\t: $this->_group_get_type($type);\n\n\t\t$wh_in = array(\n\t\t\t'condition' => $prefix.$key.$not.' IN('.implode(', ', $wh_in).')',\n\t\t\t'value' => NULL,\n\t\t\t'escape' => $escape\n\t\t);\n\n\t\t$this->{$qb_key}[] = $wh_in;\n\t\tif ($this->qb_caching === TRUE)\n\t\t{\n\t\t\t$this->{$qb_cache_key}[] = $wh_in;\n\t\t\t$this->qb_cache_exists[] = substr($qb_key, 3);\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * LIKE\n\t *\n\t * Generates a %LIKE% portion of the query.\n\t * Separates multiple calls with 'AND'.\n\t *\n\t * @param\tmixed\t$field\n\t * @param\tstring\t$match\n\t * @param\tstring\t$side\n\t * @param\tbool\t$escape\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function like($field, $match = '', $side = 'both', $escape = NULL)\n\t{\n\t\treturn $this->_like($field, $match, 'AND ', $side, '', $escape);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * NOT LIKE\n\t *\n\t * Generates a NOT LIKE portion of the query.\n\t * Separates multiple calls with 'AND'.\n\t *\n\t * @param\tmixed\t$field\n\t * @param\tstring\t$match\n\t * @param\tstring\t$side\n\t * @param\tbool\t$escape\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function not_like($field, $match = '', $side = 'both', $escape = NULL)\n\t{\n\t\treturn $this->_like($field, $match, 'AND ', $side, 'NOT', $escape);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * OR LIKE\n\t *\n\t * Generates a %LIKE% portion of the query.\n\t * Separates multiple calls with 'OR'.\n\t *\n\t * @param\tmixed\t$field\n\t * @param\tstring\t$match\n\t * @param\tstring\t$side\n\t * @param\tbool\t$escape\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function or_like($field, $match = '', $side = 'both', $escape = NULL)\n\t{\n\t\treturn $this->_like($field, $match, 'OR ', $side, '', $escape);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * OR NOT LIKE\n\t *\n\t * Generates a NOT LIKE portion of the query.\n\t * Separates multiple calls with 'OR'.\n\t *\n\t * @param\tmixed\t$field\n\t * @param\tstring\t$match\n\t * @param\tstring\t$side\n\t * @param\tbool\t$escape\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function or_not_like($field, $match = '', $side = 'both', $escape = NULL)\n\t{\n\t\treturn $this->_like($field, $match, 'OR ', $side, 'NOT', $escape);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Internal LIKE\n\t *\n\t * @used-by\tlike()\n\t * @used-by\tor_like()\n\t * @used-by\tnot_like()\n\t * @used-by\tor_not_like()\n\t *\n\t * @param\tmixed\t$field\n\t * @param\tstring\t$match\n\t * @param\tstring\t$type\n\t * @param\tstring\t$side\n\t * @param\tstring\t$not\n\t * @param\tbool\t$escape\n\t * @return\tCI_DB_query_builder\n\t */\n\tprotected function _like($field, $match = '', $type = 'AND ', $side = 'both', $not = '', $escape = NULL)\n\t{\n\t\tif ( ! is_array($field))\n\t\t{\n\t\t\t$field = array($field => $match);\n\t\t}\n\n\t\tis_bool($escape) OR $escape = $this->_protect_identifiers;\n\t\t// lowercase $side in case somebody writes e.g. 'BEFORE' instead of 'before' (doh)\n\t\t$side = strtolower($side);\n\n\t\tforeach ($field as $k => $v)\n\t\t{\n\t\t\t$prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0)\n\t\t\t\t? $this->_group_get_type('') : $this->_group_get_type($type);\n\n\t\t\tif ($escape === TRUE)\n\t\t\t{\n\t\t\t\t$v = $this->escape_like_str($v);\n\t\t\t}\n\n\t\t\tswitch ($side)\n\t\t\t{\n\t\t\t\tcase 'none':\n\t\t\t\t\t$v = \"'{$v}'\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'before':\n\t\t\t\t\t$v = \"'%{$v}'\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'after':\n\t\t\t\t\t$v = \"'{$v}%'\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'both':\n\t\t\t\tdefault:\n\t\t\t\t\t$v = \"'%{$v}%'\";\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// some platforms require an escape sequence definition for LIKE wildcards\n\t\t\tif ($escape === TRUE && $this->_like_escape_str !== '')\n\t\t\t{\n\t\t\t\t$v .= sprintf($this->_like_escape_str, $this->_like_escape_chr);\n\t\t\t}\n\n\t\t\t$qb_where = array('condition' => \"{$prefix} {$k} {$not} LIKE {$v}\", 'value' => NULL, 'escape' => $escape);\n\t\t\t$this->qb_where[] = $qb_where;\n\t\t\tif ($this->qb_caching === TRUE)\n\t\t\t{\n\t\t\t\t$this->qb_cache_where[] = $qb_where;\n\t\t\t\t$this->qb_cache_exists[] = 'where';\n\t\t\t}\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Starts a query group.\n\t *\n\t * @param\tstring\t$not\t(Internal use only)\n\t * @param\tstring\t$type\t(Internal use only)\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function group_start($not = '', $type = 'AND ')\n\t{\n\t\t$type = $this->_group_get_type($type);\n\n\t\t$this->qb_where_group_started = TRUE;\n\t\t$prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0) ? '' : $type;\n\t\t$where = array(\n\t\t\t'condition' => $prefix.$not.str_repeat(' ', ++$this->qb_where_group_count).' (',\n\t\t\t'value' => NULL,\n\t\t\t'escape' => FALSE\n\t\t);\n\n\t\t$this->qb_where[] = $where;\n\t\tif ($this->qb_caching)\n\t\t{\n\t\t\t$this->qb_cache_where[] = $where;\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Starts a query group, but ORs the group\n\t *\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function or_group_start()\n\t{\n\t\treturn $this->group_start('', 'OR ');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Starts a query group, but NOTs the group\n\t *\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function not_group_start()\n\t{\n\t\treturn $this->group_start('NOT ', 'AND ');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Starts a query group, but OR NOTs the group\n\t *\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function or_not_group_start()\n\t{\n\t\treturn $this->group_start('NOT ', 'OR ');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Ends a query group\n\t *\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function group_end()\n\t{\n\t\t$this->qb_where_group_started = FALSE;\n\t\t$where = array(\n\t\t\t'condition' => str_repeat(' ', $this->qb_where_group_count--).')',\n\t\t\t'value' => NULL,\n\t\t\t'escape' => FALSE\n\t\t);\n\n\t\t$this->qb_where[] = $where;\n\t\tif ($this->qb_caching)\n\t\t{\n\t\t\t$this->qb_cache_where[] = $where;\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Group_get_type\n\t *\n\t * @used-by\tgroup_start()\n\t * @used-by\t_like()\n\t * @used-by\t_wh()\n\t * @used-by\t_where_in()\n\t *\n\t * @param\tstring\t$type\n\t * @return\tstring\n\t */\n\tprotected function _group_get_type($type)\n\t{\n\t\tif ($this->qb_where_group_started)\n\t\t{\n\t\t\t$type = '';\n\t\t\t$this->qb_where_group_started = FALSE;\n\t\t}\n\n\t\treturn $type;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * GROUP BY\n\t *\n\t * @param\tmixed\t$by\n\t * @param\tbool\t$escape\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function group_by($by, $escape = NULL)\n\t{\n\t\tis_bool($escape) OR $escape = $this->_protect_identifiers;\n\n\t\tif (is_string($by))\n\t\t{\n\t\t\t$by = ($escape === TRUE)\n\t\t\t\t? explode(',', $by)\n\t\t\t\t: array($by);\n\t\t}\n\n\t\tforeach ($by as $val)\n\t\t{\n\t\t\t$val = trim($val);\n\n\t\t\tif ($val !== '')\n\t\t\t{\n\t\t\t\t$val = array('field' => $val, 'escape' => $escape);\n\n\t\t\t\t$this->qb_groupby[] = $val;\n\t\t\t\tif ($this->qb_caching === TRUE)\n\t\t\t\t{\n\t\t\t\t\t$this->qb_cache_groupby[] = $val;\n\t\t\t\t\t$this->qb_cache_exists[] = 'groupby';\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * HAVING\n\t *\n\t * Separates multiple calls with 'AND'.\n\t *\n\t * @param\tstring\t$key\n\t * @param\tstring\t$value\n\t * @param\tbool\t$escape\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function having($key, $value = NULL, $escape = NULL)\n\t{\n\t\treturn $this->_wh('qb_having', $key, $value, 'AND ', $escape);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * OR HAVING\n\t *\n\t * Separates multiple calls with 'OR'.\n\t *\n\t * @param\tstring\t$key\n\t * @param\tstring\t$value\n\t * @param\tbool\t$escape\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function or_having($key, $value = NULL, $escape = NULL)\n\t{\n\t\treturn $this->_wh('qb_having', $key, $value, 'OR ', $escape);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ORDER BY\n\t *\n\t * @param\tstring\t$orderby\n\t * @param\tstring\t$direction\tASC, DESC or RANDOM\n\t * @param\tbool\t$escape\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function order_by($orderby, $direction = '', $escape = NULL)\n\t{\n\t\t$direction = strtoupper(trim($direction));\n\n\t\tif ($direction === 'RANDOM')\n\t\t{\n\t\t\t$direction = '';\n\n\t\t\t// Do we have a seed value?\n\t\t\t$orderby = ctype_digit((string) $orderby)\n\t\t\t\t? sprintf($this->_random_keyword[1], $orderby)\n\t\t\t\t: $this->_random_keyword[0];\n\t\t}\n\t\telseif (empty($orderby))\n\t\t{\n\t\t\treturn $this;\n\t\t}\n\t\telseif ($direction !== '')\n\t\t{\n\t\t\t$direction = in_array($direction, array('ASC', 'DESC'), TRUE) ? ' '.$direction : '';\n\t\t}\n\n\t\tis_bool($escape) OR $escape = $this->_protect_identifiers;\n\n\t\tif ($escape === FALSE)\n\t\t{\n\t\t\t$qb_orderby[] = array('field' => $orderby, 'direction' => $direction, 'escape' => FALSE);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$qb_orderby = array();\n\t\t\tforeach (explode(',', $orderby) as $field)\n\t\t\t{\n\t\t\t\t$qb_orderby[] = ($direction === '' && preg_match('/\\s+(ASC|DESC)$/i', rtrim($field), $match, PREG_OFFSET_CAPTURE))\n\t\t\t\t\t? array('field' => ltrim(substr($field, 0, $match[0][1])), 'direction' => ' '.$match[1][0], 'escape' => TRUE)\n\t\t\t\t\t: array('field' => trim($field), 'direction' => $direction, 'escape' => TRUE);\n\t\t\t}\n\t\t}\n\n\t\t$this->qb_orderby = array_merge($this->qb_orderby, $qb_orderby);\n\t\tif ($this->qb_caching === TRUE)\n\t\t{\n\t\t\t$this->qb_cache_orderby = array_merge($this->qb_cache_orderby, $qb_orderby);\n\t\t\t$this->qb_cache_exists[] = 'orderby';\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * LIMIT\n\t *\n\t * @param\tint\t$value\tLIMIT value\n\t * @param\tint\t$offset\tOFFSET value\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function limit($value, $offset = 0)\n\t{\n\t\tis_null($value) OR $this->qb_limit = (int) $value;\n\t\tempty($offset) OR $this->qb_offset = (int) $offset;\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Sets the OFFSET value\n\t *\n\t * @param\tint\t$offset\tOFFSET value\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function offset($offset)\n\t{\n\t\tempty($offset) OR $this->qb_offset = (int) $offset;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * LIMIT string\n\t *\n\t * Generates a platform-specific LIMIT clause.\n\t *\n\t * @param\tstring\t$sql\tSQL Query\n\t * @return\tstring\n\t */\n\tprotected function _limit($sql)\n\t{\n\t\treturn $sql.' LIMIT '.($this->qb_offset ? $this->qb_offset.', ' : '').(int) $this->qb_limit;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * The \"set\" function.\n\t *\n\t * Allows key/value pairs to be set for inserting or updating\n\t *\n\t * @param\tmixed\n\t * @param\tstring\n\t * @param\tbool\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function set($key, $value = '', $escape = NULL)\n\t{\n\t\t$key = $this->_object_to_array($key);\n\n\t\tif ( ! is_array($key))\n\t\t{\n\t\t\t$key = array($key => $value);\n\t\t}\n\n\t\tis_bool($escape) OR $escape = $this->_protect_identifiers;\n\n\t\tforeach ($key as $k => $v)\n\t\t{\n\t\t\t$this->qb_set[$this->protect_identifiers($k, FALSE, $escape)] = ($escape)\n\t\t\t\t? $this->escape($v) : $v;\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get SELECT query string\n\t *\n\t * Compiles a SELECT query string and returns the sql.\n\t *\n\t * @param\tstring\tthe table name to select from (optional)\n\t * @param\tbool\tTRUE: resets QB values; FALSE: leave QB values alone\n\t * @return\tstring\n\t */\n\tpublic function get_compiled_select($table = '', $reset = TRUE)\n\t{\n\t\tif ($table !== '')\n\t\t{\n\t\t\t$this->_track_aliases($table);\n\t\t\t$this->from($table);\n\t\t}\n\n\t\t$select = $this->_compile_select();\n\n\t\tif ($reset === TRUE)\n\t\t{\n\t\t\t$this->_reset_select();\n\t\t}\n\n\t\treturn $select;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get\n\t *\n\t * Compiles the select statement based on the other functions called\n\t * and runs the query\n\t *\n\t * @param\tstring\tthe table\n\t * @param\tstring\tthe limit clause\n\t * @param\tstring\tthe offset clause\n\t * @return\tCI_DB_result\n\t */\n\tpublic function get($table = '', $limit = NULL, $offset = NULL)\n\t{\n\t\tif ($table !== '')\n\t\t{\n\t\t\t$this->_track_aliases($table);\n\t\t\t$this->from($table);\n\t\t}\n\n\t\tif ( ! empty($limit))\n\t\t{\n\t\t\t$this->limit($limit, $offset);\n\t\t}\n\n\t\t$result = $this->query($this->_compile_select());\n\t\t$this->_reset_select();\n\t\treturn $result;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * \"Count All Results\" query\n\t *\n\t * Generates a platform-specific query string that counts all records\n\t * returned by an Query Builder query.\n\t *\n\t * @param\tstring\n\t * @param\tbool\tthe reset clause\n\t * @return\tint\n\t */\n\tpublic function count_all_results($table = '', $reset = TRUE)\n\t{\n\t\tif ($table !== '')\n\t\t{\n\t\t\t$this->_track_aliases($table);\n\t\t\t$this->from($table);\n\t\t}\n\n\t\t// ORDER BY usage is often problematic here (most notably\n\t\t// on Microsoft SQL Server) and ultimately unnecessary\n\t\t// for selecting COUNT(*) ...\n\t\t$qb_orderby       = $this->qb_orderby;\n\t\t$qb_cache_orderby = $this->qb_cache_orderby;\n\t\t$this->qb_orderby = $this->qb_cache_orderby = array();\n\n\t\t$result = ($this->qb_distinct === TRUE OR ! empty($this->qb_groupby) OR ! empty($this->qb_cache_groupby) OR ! empty($this->qb_having) OR $this->qb_limit OR $this->qb_offset)\n\t\t\t? $this->query($this->_count_string.$this->protect_identifiers('numrows').\"\\nFROM (\\n\".$this->_compile_select().\"\\n) CI_count_all_results\")\n\t\t\t: $this->query($this->_compile_select($this->_count_string.$this->protect_identifiers('numrows')));\n\n\t\tif ($reset === TRUE)\n\t\t{\n\t\t\t$this->_reset_select();\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$this->qb_orderby       = $qb_orderby;\n\t\t\t$this->qb_cache_orderby = $qb_cache_orderby;\n\t\t}\n\n\t\tif ($result->num_rows() === 0)\n\t\t{\n\t\t\treturn 0;\n\t\t}\n\n\t\t$row = $result->row();\n\t\treturn (int) $row->numrows;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * get_where()\n\t *\n\t * Allows the where clause, limit and offset to be added directly\n\t *\n\t * @param\tstring\t$table\n\t * @param\tstring\t$where\n\t * @param\tint\t$limit\n\t * @param\tint\t$offset\n\t * @return\tCI_DB_result\n\t */\n\tpublic function get_where($table = '', $where = NULL, $limit = NULL, $offset = NULL)\n\t{\n\t\tif ($table !== '')\n\t\t{\n\t\t\t$this->from($table);\n\t\t}\n\n\t\tif ($where !== NULL)\n\t\t{\n\t\t\t$this->where($where);\n\t\t}\n\n\t\tif ( ! empty($limit))\n\t\t{\n\t\t\t$this->limit($limit, $offset);\n\t\t}\n\n\t\t$result = $this->query($this->_compile_select());\n\t\t$this->_reset_select();\n\t\treturn $result;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert_Batch\n\t *\n\t * Compiles batch insert strings and runs the queries\n\t *\n\t * @param\tstring\t$table\tTable to insert into\n\t * @param\tarray\t$set \tAn associative array of insert values\n\t * @param\tbool\t$escape\tWhether to escape values and identifiers\n\t * @return\tint\tNumber of rows inserted or FALSE on failure\n\t */\n\tpublic function insert_batch($table, $set = NULL, $escape = NULL, $batch_size = 100)\n\t{\n\t\tif ($set === NULL)\n\t\t{\n\t\t\tif (empty($this->qb_set))\n\t\t\t{\n\t\t\t\treturn ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (empty($set))\n\t\t\t{\n\t\t\t\treturn ($this->db_debug) ? $this->display_error('insert_batch() called with no data') : FALSE;\n\t\t\t}\n\n\t\t\t$this->set_insert_batch($set, '', $escape);\n\t\t}\n\n\t\tif (strlen($table) === 0)\n\t\t{\n\t\t\tif ( ! isset($this->qb_from[0]))\n\t\t\t{\n\t\t\t\treturn ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;\n\t\t\t}\n\n\t\t\t$table = $this->qb_from[0];\n\t\t}\n\n\t\t// Batch this baby\n\t\t$affected_rows = 0;\n\t\tfor ($i = 0, $total = count($this->qb_set); $i < $total; $i += $batch_size)\n\t\t{\n\t\t\tif ($this->query($this->_insert_batch($this->protect_identifiers($table, TRUE, $escape, FALSE), $this->qb_keys, array_slice($this->qb_set, $i, $batch_size))))\n\t\t\t{\n\t\t\t\t$affected_rows += $this->affected_rows();\n\t\t\t}\n\t\t}\n\n\t\t$this->_reset_write();\n\t\treturn $affected_rows;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert batch statement\n\t *\n\t * Generates a platform-specific insert string from the supplied data.\n\t *\n\t * @param\tstring\t$table\tTable name\n\t * @param\tarray\t$keys\tINSERT keys\n\t * @param\tarray\t$values\tINSERT values\n\t * @return\tstring\n\t */\n\tprotected function _insert_batch($table, $keys, $values)\n\t{\n\t\treturn 'INSERT INTO '.$table.' ('.implode(', ', $keys).') VALUES '.implode(', ', $values);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * The \"set_insert_batch\" function.  Allows key/value pairs to be set for batch inserts\n\t *\n\t * @param\tmixed\n\t * @param\tstring\n\t * @param\tbool\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function set_insert_batch($key, $value = '', $escape = NULL)\n\t{\n\t\t$key = $this->_object_to_array_batch($key);\n\n\t\tif ( ! is_array($key))\n\t\t{\n\t\t\t$key = array($key => $value);\n\t\t}\n\n\t\tis_bool($escape) OR $escape = $this->_protect_identifiers;\n\n\t\t$keys = array_keys($this->_object_to_array(reset($key)));\n\t\tsort($keys);\n\n\t\tforeach ($key as $row)\n\t\t{\n\t\t\t$row = $this->_object_to_array($row);\n\t\t\tif (count(array_diff($keys, array_keys($row))) > 0 OR count(array_diff(array_keys($row), $keys)) > 0)\n\t\t\t{\n\t\t\t\t// batch function above returns an error on an empty array\n\t\t\t\t$this->qb_set[] = array();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tksort($row); // puts $row in the same order as our keys\n\n\t\t\tif ($escape !== FALSE)\n\t\t\t{\n\t\t\t\t$clean = array();\n\t\t\t\tforeach ($row as $value)\n\t\t\t\t{\n\t\t\t\t\t$clean[] = $this->escape($value);\n\t\t\t\t}\n\n\t\t\t\t$row = $clean;\n\t\t\t}\n\n\t\t\t$this->qb_set[] = '('.implode(',', $row).')';\n\t\t}\n\n\t\tforeach ($keys as $k)\n\t\t{\n\t\t\t$this->qb_keys[] = $this->protect_identifiers($k, FALSE, $escape);\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get INSERT query string\n\t *\n\t * Compiles an insert query and returns the sql\n\t *\n\t * @param\tstring\tthe table to insert into\n\t * @param\tbool\tTRUE: reset QB values; FALSE: leave QB values alone\n\t * @return\tstring\n\t */\n\tpublic function get_compiled_insert($table = '', $reset = TRUE)\n\t{\n\t\tif ($this->_validate_insert($table) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$sql = $this->_insert(\n\t\t\t$this->protect_identifiers(\n\t\t\t\t$this->qb_from[0], TRUE, NULL, FALSE\n\t\t\t),\n\t\t\tarray_keys($this->qb_set),\n\t\t\tarray_values($this->qb_set)\n\t\t);\n\n\t\tif ($reset === TRUE)\n\t\t{\n\t\t\t$this->_reset_write();\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert\n\t *\n\t * Compiles an insert string and runs the query\n\t *\n\t * @param\tstring\tthe table to insert data into\n\t * @param\tarray\tan associative array of insert values\n\t * @param\tbool\t$escape\tWhether to escape values and identifiers\n\t * @return\tbool\tTRUE on success, FALSE on failure\n\t */\n\tpublic function insert($table = '', $set = NULL, $escape = NULL)\n\t{\n\t\tif ($set !== NULL)\n\t\t{\n\t\t\t$this->set($set, '', $escape);\n\t\t}\n\n\t\tif ($this->_validate_insert($table) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$sql = $this->_insert(\n\t\t\t$this->protect_identifiers(\n\t\t\t\t$this->qb_from[0], TRUE, $escape, FALSE\n\t\t\t),\n\t\t\tarray_keys($this->qb_set),\n\t\t\tarray_values($this->qb_set)\n\t\t);\n\n\t\t$this->_reset_write();\n\t\treturn $this->query($sql);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Validate Insert\n\t *\n\t * This method is used by both insert() and get_compiled_insert() to\n\t * validate that the there data is actually being set and that table\n\t * has been chosen to be inserted into.\n\t *\n\t * @param\tstring\tthe table to insert data into\n\t * @return\tstring\n\t */\n\tprotected function _validate_insert($table = '')\n\t{\n\t\tif (count($this->qb_set) === 0)\n\t\t{\n\t\t\treturn ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;\n\t\t}\n\n\t\tif ($table !== '')\n\t\t{\n\t\t\t$this->qb_from[0] = $table;\n\t\t}\n\t\telseif ( ! isset($this->qb_from[0]))\n\t\t{\n\t\t\treturn ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Replace\n\t *\n\t * Compiles an replace into string and runs the query\n\t *\n\t * @param\tstring\tthe table to replace data into\n\t * @param\tarray\tan associative array of insert values\n\t * @return\tbool\tTRUE on success, FALSE on failure\n\t */\n\tpublic function replace($table = '', $set = NULL)\n\t{\n\t\tif ($set !== NULL)\n\t\t{\n\t\t\t$this->set($set);\n\t\t}\n\n\t\tif (count($this->qb_set) === 0)\n\t\t{\n\t\t\treturn ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;\n\t\t}\n\n\t\tif ($table === '')\n\t\t{\n\t\t\tif ( ! isset($this->qb_from[0]))\n\t\t\t{\n\t\t\t\treturn ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;\n\t\t\t}\n\n\t\t\t$table = $this->qb_from[0];\n\t\t}\n\n\t\t$sql = $this->_replace($this->protect_identifiers($table, TRUE, NULL, FALSE), array_keys($this->qb_set), array_values($this->qb_set));\n\n\t\t$this->_reset_write();\n\t\treturn $this->query($sql);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Replace statement\n\t *\n\t * Generates a platform-specific replace string from the supplied data\n\t *\n\t * @param\tstring\tthe table name\n\t * @param\tarray\tthe insert keys\n\t * @param\tarray\tthe insert values\n\t * @return\tstring\n\t */\n\tprotected function _replace($table, $keys, $values)\n\t{\n\t\treturn 'REPLACE INTO '.$table.' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * FROM tables\n\t *\n\t * Groups tables in FROM clauses if needed, so there is no confusion\n\t * about operator precedence.\n\t *\n\t * Note: This is only used (and overridden) by MySQL and CUBRID.\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _from_tables()\n\t{\n\t\treturn implode(', ', $this->qb_from);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get UPDATE query string\n\t *\n\t * Compiles an update query and returns the sql\n\t *\n\t * @param\tstring\tthe table to update\n\t * @param\tbool\tTRUE: reset QB values; FALSE: leave QB values alone\n\t * @return\tstring\n\t */\n\tpublic function get_compiled_update($table = '', $reset = TRUE)\n\t{\n\t\t// Combine any cached components with the current statements\n\t\t$this->_merge_cache();\n\n\t\tif ($this->_validate_update($table) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$sql = $this->_update($this->qb_from[0], $this->qb_set);\n\n\t\tif ($reset === TRUE)\n\t\t{\n\t\t\t$this->_reset_write();\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * UPDATE\n\t *\n\t * Compiles an update string and runs the query.\n\t *\n\t * @param\tstring\t$table\n\t * @param\tarray\t$set\tAn associative array of update values\n\t * @param\tmixed\t$where\n\t * @param\tint\t$limit\n\t * @return\tbool\tTRUE on success, FALSE on failure\n\t */\n\tpublic function update($table = '', $set = NULL, $where = NULL, $limit = NULL)\n\t{\n\t\t// Combine any cached components with the current statements\n\t\t$this->_merge_cache();\n\n\t\tif ($set !== NULL)\n\t\t{\n\t\t\t$this->set($set);\n\t\t}\n\n\t\tif ($this->_validate_update($table) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif ($where !== NULL)\n\t\t{\n\t\t\t$this->where($where);\n\t\t}\n\n\t\tif ( ! empty($limit))\n\t\t{\n\t\t\t$this->limit($limit);\n\t\t}\n\n\t\t$sql = $this->_update($this->qb_from[0], $this->qb_set);\n\t\t$this->_reset_write();\n\t\treturn $this->query($sql);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Validate Update\n\t *\n\t * This method is used by both update() and get_compiled_update() to\n\t * validate that data is actually being set and that a table has been\n\t * chosen to be update.\n\t *\n\t * @param\tstring\tthe table to update data on\n\t * @return\tbool\n\t */\n\tprotected function _validate_update($table)\n\t{\n\t\tif (count($this->qb_set) === 0)\n\t\t{\n\t\t\treturn ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;\n\t\t}\n\n\t\tif ($table !== '')\n\t\t{\n\t\t\t$this->qb_from = array($this->protect_identifiers($table, TRUE, NULL, FALSE));\n\t\t}\n\t\telseif ( ! isset($this->qb_from[0]))\n\t\t{\n\t\t\treturn ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Update_Batch\n\t *\n\t * Compiles an update string and runs the query\n\t *\n\t * @param\tstring\tthe table to retrieve the results from\n\t * @param\tarray\tan associative array of update values\n\t * @param\tstring\tthe where key\n\t * @return\tint\tnumber of rows affected or FALSE on failure\n\t */\n\tpublic function update_batch($table, $set = NULL, $index = NULL, $batch_size = 100)\n\t{\n\t\t// Combine any cached components with the current statements\n\t\t$this->_merge_cache();\n\n\t\tif ($index === NULL)\n\t\t{\n\t\t\treturn ($this->db_debug) ? $this->display_error('db_must_use_index') : FALSE;\n\t\t}\n\n\t\tif ($set === NULL)\n\t\t{\n\t\t\tif (empty($this->qb_set_ub))\n\t\t\t{\n\t\t\t\treturn ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (empty($set))\n\t\t\t{\n\t\t\t\treturn ($this->db_debug) ? $this->display_error('update_batch() called with no data') : FALSE;\n\t\t\t}\n\n\t\t\t$this->set_update_batch($set, $index);\n\t\t}\n\n\t\tif (strlen($table) === 0)\n\t\t{\n\t\t\tif ( ! isset($this->qb_from[0]))\n\t\t\t{\n\t\t\t\treturn ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;\n\t\t\t}\n\n\t\t\t$table = $this->qb_from[0];\n\t\t}\n\n\t\t// Batch this baby\n\t\t$affected_rows = 0;\n\t\tfor ($i = 0, $total = count($this->qb_set_ub); $i < $total; $i += $batch_size)\n\t\t{\n\t\t\tif ($this->query($this->_update_batch($this->protect_identifiers($table, TRUE, NULL, FALSE), array_slice($this->qb_set_ub, $i, $batch_size), $index)))\n\t\t\t{\n\t\t\t\t$affected_rows += $this->affected_rows();\n\t\t\t}\n\n\t\t\t$this->qb_where = array();\n\t\t}\n\n\t\t$this->_reset_write();\n\t\treturn $affected_rows;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Update_Batch statement\n\t *\n\t * Generates a platform-specific batch update string from the supplied data\n\t *\n\t * @param\tstring\t$table\tTable name\n\t * @param\tarray\t$values\tUpdate data\n\t * @param\tstring\t$index\tWHERE key\n\t * @return\tstring\n\t */\n\tprotected function _update_batch($table, $values, $index)\n\t{\n\t\t$ids = array();\n\t\tforeach ($values as $key => $val)\n\t\t{\n\t\t\t$ids[] = $val[$index]['value'];\n\n\t\t\tforeach (array_keys($val) as $field)\n\t\t\t{\n\t\t\t\tif ($field !== $index)\n\t\t\t\t{\n\t\t\t\t\t$final[$val[$field]['field']][] = 'WHEN '.$val[$index]['field'].' = '.$val[$index]['value'].' THEN '.$val[$field]['value'];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$cases = '';\n\t\tforeach ($final as $k => $v)\n\t\t{\n\t\t\t$cases .= $k.\" = CASE \\n\"\n\t\t\t\t.implode(\"\\n\", $v).\"\\n\"\n\t\t\t\t.'ELSE '.$k.' END, ';\n\t\t}\n\n\t\t$this->where($val[$index]['field'].' IN('.implode(',', $ids).')', NULL, FALSE);\n\n\t\treturn 'UPDATE '.$table.' SET '.substr($cases, 0, -2).$this->_compile_wh('qb_where');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * The \"set_update_batch\" function.  Allows key/value pairs to be set for batch updating\n\t *\n\t * @param\tarray\n\t * @param\tstring\n\t * @param\tbool\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function set_update_batch($key, $index = '', $escape = NULL)\n\t{\n\t\t$key = $this->_object_to_array_batch($key);\n\n\t\tif ( ! is_array($key))\n\t\t{\n\t\t\t// @todo error\n\t\t}\n\n\t\tis_bool($escape) OR $escape = $this->_protect_identifiers;\n\n\t\tforeach ($key as $k => $v)\n\t\t{\n\t\t\t$index_set = FALSE;\n\t\t\t$clean = array();\n\t\t\tforeach ($v as $k2 => $v2)\n\t\t\t{\n\t\t\t\tif ($k2 === $index)\n\t\t\t\t{\n\t\t\t\t\t$index_set = TRUE;\n\t\t\t\t}\n\n\t\t\t\t$clean[$k2] = array(\n\t\t\t\t\t'field'  => $this->protect_identifiers($k2, FALSE, $escape),\n\t\t\t\t\t'value'  => ($escape === FALSE ? $v2 : $this->escape($v2))\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif ($index_set === FALSE)\n\t\t\t{\n\t\t\t\treturn $this->display_error('db_batch_missing_index');\n\t\t\t}\n\n\t\t\t$this->qb_set_ub[] = $clean;\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Empty Table\n\t *\n\t * Compiles a delete string and runs \"DELETE FROM table\"\n\t *\n\t * @param\tstring\tthe table to empty\n\t * @return\tbool\tTRUE on success, FALSE on failure\n\t */\n\tpublic function empty_table($table = '')\n\t{\n\t\tif ($table === '')\n\t\t{\n\t\t\tif ( ! isset($this->qb_from[0]))\n\t\t\t{\n\t\t\t\treturn ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;\n\t\t\t}\n\n\t\t\t$table = $this->qb_from[0];\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$table = $this->protect_identifiers($table, TRUE, NULL, FALSE);\n\t\t}\n\n\t\t$sql = $this->_delete($table);\n\t\t$this->_reset_write();\n\t\treturn $this->query($sql);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Truncate\n\t *\n\t * Compiles a truncate string and runs the query\n\t * If the database does not support the truncate() command\n\t * This function maps to \"DELETE FROM table\"\n\t *\n\t * @param\tstring\tthe table to truncate\n\t * @return\tbool\tTRUE on success, FALSE on failure\n\t */\n\tpublic function truncate($table = '')\n\t{\n\t\tif ($table === '')\n\t\t{\n\t\t\tif ( ! isset($this->qb_from[0]))\n\t\t\t{\n\t\t\t\treturn ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;\n\t\t\t}\n\n\t\t\t$table = $this->qb_from[0];\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$table = $this->protect_identifiers($table, TRUE, NULL, FALSE);\n\t\t}\n\n\t\t$sql = $this->_truncate($table);\n\t\t$this->_reset_write();\n\t\treturn $this->query($sql);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Truncate statement\n\t *\n\t * Generates a platform-specific truncate string from the supplied data\n\t *\n\t * If the database does not support the truncate() command,\n\t * then this method maps to 'DELETE FROM table'\n\t *\n\t * @param\tstring\tthe table name\n\t * @return\tstring\n\t */\n\tprotected function _truncate($table)\n\t{\n\t\treturn 'TRUNCATE '.$table;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get DELETE query string\n\t *\n\t * Compiles a delete query string and returns the sql\n\t *\n\t * @param\tstring\tthe table to delete from\n\t * @param\tbool\tTRUE: reset QB values; FALSE: leave QB values alone\n\t * @return\tstring\n\t */\n\tpublic function get_compiled_delete($table = '', $reset = TRUE)\n\t{\n\t\t$this->return_delete_sql = TRUE;\n\t\t$sql = $this->delete($table, '', NULL, $reset);\n\t\t$this->return_delete_sql = FALSE;\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Delete\n\t *\n\t * Compiles a delete string and runs the query\n\t *\n\t * @param\tmixed\tthe table(s) to delete from. String or array\n\t * @param\tmixed\tthe where clause\n\t * @param\tmixed\tthe limit clause\n\t * @param\tbool\n\t * @return\tmixed\n\t */\n\tpublic function delete($table = '', $where = '', $limit = NULL, $reset_data = TRUE)\n\t{\n\t\t// Combine any cached components with the current statements\n\t\t$this->_merge_cache();\n\n\t\tif ($table === '')\n\t\t{\n\t\t\tif ( ! isset($this->qb_from[0]))\n\t\t\t{\n\t\t\t\treturn ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;\n\t\t\t}\n\n\t\t\t$table = $this->qb_from[0];\n\t\t}\n\t\telseif (is_array($table))\n\t\t{\n\t\t\tempty($where) && $reset_data = FALSE;\n\n\t\t\tforeach ($table as $single_table)\n\t\t\t{\n\t\t\t\t$this->delete($single_table, $where, $limit, $reset_data);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$table = $this->protect_identifiers($table, TRUE, NULL, FALSE);\n\t\t}\n\n\t\tif ($where !== '')\n\t\t{\n\t\t\t$this->where($where);\n\t\t}\n\n\t\tif ( ! empty($limit))\n\t\t{\n\t\t\t$this->limit($limit);\n\t\t}\n\n\t\tif (count($this->qb_where) === 0)\n\t\t{\n\t\t\treturn ($this->db_debug) ? $this->display_error('db_del_must_use_where') : FALSE;\n\t\t}\n\n\t\t$sql = $this->_delete($table);\n\t\tif ($reset_data)\n\t\t{\n\t\t\t$this->_reset_write();\n\t\t}\n\n\t\treturn ($this->return_delete_sql === TRUE) ? $sql : $this->query($sql);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Delete statement\n\t *\n\t * Generates a platform-specific delete string from the supplied data\n\t *\n\t * @param\tstring\tthe table name\n\t * @return\tstring\n\t */\n\tprotected function _delete($table)\n\t{\n\t\treturn 'DELETE FROM '.$table.$this->_compile_wh('qb_where')\n\t\t\t.($this->qb_limit !== FALSE ? ' LIMIT '.$this->qb_limit : '');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * DB Prefix\n\t *\n\t * Prepends a database prefix if one exists in configuration\n\t *\n\t * @param\tstring\tthe table\n\t * @return\tstring\n\t */\n\tpublic function dbprefix($table = '')\n\t{\n\t\tif ($table === '')\n\t\t{\n\t\t\t$this->display_error('db_table_name_required');\n\t\t}\n\n\t\treturn $this->dbprefix.$table;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set DB Prefix\n\t *\n\t * Set's the DB Prefix to something new without needing to reconnect\n\t *\n\t * @param\tstring\tthe prefix\n\t * @return\tstring\n\t */\n\tpublic function set_dbprefix($prefix = '')\n\t{\n\t\treturn $this->dbprefix = $prefix;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Track Aliases\n\t *\n\t * Used to track SQL statements written with aliased tables.\n\t *\n\t * @param\tstring\tThe table to inspect\n\t * @return\tstring\n\t */\n\tprotected function _track_aliases($table)\n\t{\n\t\tif (is_array($table))\n\t\t{\n\t\t\tforeach ($table as $t)\n\t\t\t{\n\t\t\t\t$this->_track_aliases($t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Does the string contain a comma?  If so, we need to separate\n\t\t// the string into discreet statements\n\t\tif (strpos($table, ',') !== FALSE)\n\t\t{\n\t\t\treturn $this->_track_aliases(explode(',', $table));\n\t\t}\n\n\t\t// if a table alias is used we can recognize it by a space\n\t\tif (strpos($table, ' ') !== FALSE)\n\t\t{\n\t\t\t// if the alias is written with the AS keyword, remove it\n\t\t\t$table = preg_replace('/\\s+AS\\s+/i', ' ', $table);\n\n\t\t\t// Grab the alias\n\t\t\t$table = trim(strrchr($table, ' '));\n\n\t\t\t// Store the alias, if it doesn't already exist\n\t\t\tif ( ! in_array($table, $this->qb_aliased_tables, TRUE))\n\t\t\t{\n\t\t\t\t$this->qb_aliased_tables[] = $table;\n\t\t\t\tif ($this->qb_caching === TRUE && ! in_array($table, $this->qb_cache_aliased_tables, TRUE))\n\t\t\t\t{\n\t\t\t\t\t$this->qb_cache_aliased_tables[] = $table;\n\t\t\t\t\t$this->qb_cache_exists[] = 'aliased_tables';\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Compile the SELECT statement\n\t *\n\t * Generates a query string based on which functions were used.\n\t * Should not be called directly.\n\t *\n\t * @param\tbool\t$select_override\n\t * @return\tstring\n\t */\n\tprotected function _compile_select($select_override = FALSE)\n\t{\n\t\t// Combine any cached components with the current statements\n\t\t$this->_merge_cache();\n\n\t\t// Write the \"select\" portion of the query\n\t\tif ($select_override !== FALSE)\n\t\t{\n\t\t\t$sql = $select_override;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$sql = ( ! $this->qb_distinct) ? 'SELECT ' : 'SELECT DISTINCT ';\n\n\t\t\tif (count($this->qb_select) === 0)\n\t\t\t{\n\t\t\t\t$sql .= '*';\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Cycle through the \"select\" portion of the query and prep each column name.\n\t\t\t\t// The reason we protect identifiers here rather than in the select() function\n\t\t\t\t// is because until the user calls the from() function we don't know if there are aliases\n\t\t\t\tforeach ($this->qb_select as $key => $val)\n\t\t\t\t{\n\t\t\t\t\t$no_escape = isset($this->qb_no_escape[$key]) ? $this->qb_no_escape[$key] : NULL;\n\t\t\t\t\t$this->qb_select[$key] = $this->protect_identifiers($val, FALSE, $no_escape);\n\t\t\t\t}\n\n\t\t\t\t$sql .= implode(', ', $this->qb_select);\n\t\t\t}\n\t\t}\n\n\t\t// Write the \"FROM\" portion of the query\n\t\tif (count($this->qb_from) > 0)\n\t\t{\n\t\t\t$sql .= \"\\nFROM \".$this->_from_tables();\n\t\t}\n\n\t\t// Write the \"JOIN\" portion of the query\n\t\tif (count($this->qb_join) > 0)\n\t\t{\n\t\t\t$sql .= \"\\n\".implode(\"\\n\", $this->qb_join);\n\t\t}\n\n\t\t$sql .= $this->_compile_wh('qb_where')\n\t\t\t.$this->_compile_group_by()\n\t\t\t.$this->_compile_wh('qb_having')\n\t\t\t.$this->_compile_order_by(); // ORDER BY\n\n\t\t// LIMIT\n\t\tif ($this->qb_limit !== FALSE OR $this->qb_offset)\n\t\t{\n\t\t\treturn $this->_limit($sql.\"\\n\");\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Compile WHERE, HAVING statements\n\t *\n\t * Escapes identifiers in WHERE and HAVING statements at execution time.\n\t *\n\t * Required so that aliases are tracked properly, regardless of whether\n\t * where(), or_where(), having(), or_having are called prior to from(),\n\t * join() and dbprefix is added only if needed.\n\t *\n\t * @param\tstring\t$qb_key\t'qb_where' or 'qb_having'\n\t * @return\tstring\tSQL statement\n\t */\n\tprotected function _compile_wh($qb_key)\n\t{\n\t\tif (count($this->$qb_key) > 0)\n\t\t{\n\t\t\tfor ($i = 0, $c = count($this->$qb_key); $i < $c; $i++)\n\t\t\t{\n\t\t\t\t// Is this condition already compiled?\n\t\t\t\tif (is_string($this->{$qb_key}[$i]))\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\telseif ($this->{$qb_key}[$i]['escape'] === FALSE)\n\t\t\t\t{\n\t\t\t\t\t$this->{$qb_key}[$i] = $this->{$qb_key}[$i]['condition'].(isset($this->{$qb_key}[$i]['value']) ? ' '.$this->{$qb_key}[$i]['value'] : '');\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Split multiple conditions\n\t\t\t\t$conditions = preg_split(\n\t\t\t\t\t'/((?:^|\\s+)AND\\s+|(?:^|\\s+)OR\\s+)/i',\n\t\t\t\t\t$this->{$qb_key}[$i]['condition'],\n\t\t\t\t\t-1,\n\t\t\t\t\tPREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY\n\t\t\t\t);\n\n\t\t\t\tfor ($ci = 0, $cc = count($conditions); $ci < $cc; $ci++)\n\t\t\t\t{\n\t\t\t\t\tif (($op = $this->_get_operator($conditions[$ci])) === FALSE\n\t\t\t\t\t\tOR ! preg_match('/^(\\(?)(.*)('.preg_quote($op, '/').')\\s*(.*(?<!\\)))?(\\)?)$/i', $conditions[$ci], $matches))\n\t\t\t\t\t{\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// $matches = array(\n\t\t\t\t\t//\t0 => '(test <= foo)',\t/* the whole thing */\n\t\t\t\t\t//\t1 => '(',\t\t/* optional */\n\t\t\t\t\t//\t2 => 'test',\t\t/* the field name */\n\t\t\t\t\t//\t3 => ' <= ',\t\t/* $op */\n\t\t\t\t\t//\t4 => 'foo',\t\t/* optional, if $op is e.g. 'IS NULL' */\n\t\t\t\t\t//\t5 => ')'\t\t/* optional */\n\t\t\t\t\t// );\n\n\t\t\t\t\tif ( ! empty($matches[4]))\n\t\t\t\t\t{\n\t\t\t\t\t\t$this->_is_literal($matches[4]) OR $matches[4] = $this->protect_identifiers(trim($matches[4]));\n\t\t\t\t\t\t$matches[4] = ' '.$matches[4];\n\t\t\t\t\t}\n\n\t\t\t\t\t$conditions[$ci] = $matches[1].$this->protect_identifiers(trim($matches[2]))\n\t\t\t\t\t\t.' '.trim($matches[3]).$matches[4].$matches[5];\n\t\t\t\t}\n\n\t\t\t\t$this->{$qb_key}[$i] = implode('', $conditions).(isset($this->{$qb_key}[$i]['value']) ? ' '.$this->{$qb_key}[$i]['value'] : '');\n\t\t\t}\n\n\t\t\treturn ($qb_key === 'qb_having' ? \"\\nHAVING \" : \"\\nWHERE \")\n\t\t\t\t.implode(\"\\n\", $this->$qb_key);\n\t\t}\n\n\t\treturn '';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Compile GROUP BY\n\t *\n\t * Escapes identifiers in GROUP BY statements at execution time.\n\t *\n\t * Required so that aliases are tracked properly, regardless of whether\n\t * group_by() is called prior to from(), join() and dbprefix is added\n\t * only if needed.\n\t *\n\t * @return\tstring\tSQL statement\n\t */\n\tprotected function _compile_group_by()\n\t{\n\t\tif (count($this->qb_groupby) > 0)\n\t\t{\n\t\t\tfor ($i = 0, $c = count($this->qb_groupby); $i < $c; $i++)\n\t\t\t{\n\t\t\t\t// Is it already compiled?\n\t\t\t\tif (is_string($this->qb_groupby[$i]))\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t$this->qb_groupby[$i] = ($this->qb_groupby[$i]['escape'] === FALSE OR $this->_is_literal($this->qb_groupby[$i]['field']))\n\t\t\t\t\t? $this->qb_groupby[$i]['field']\n\t\t\t\t\t: $this->protect_identifiers($this->qb_groupby[$i]['field']);\n\t\t\t}\n\n\t\t\treturn \"\\nGROUP BY \".implode(', ', $this->qb_groupby);\n\t\t}\n\n\t\treturn '';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Compile ORDER BY\n\t *\n\t * Escapes identifiers in ORDER BY statements at execution time.\n\t *\n\t * Required so that aliases are tracked properly, regardless of whether\n\t * order_by() is called prior to from(), join() and dbprefix is added\n\t * only if needed.\n\t *\n\t * @return\tstring\tSQL statement\n\t */\n\tprotected function _compile_order_by()\n\t{\n\t\tif (empty($this->qb_orderby))\n\t\t{\n\t\t\treturn '';\n\t\t}\n\n\t\tfor ($i = 0, $c = count($this->qb_orderby); $i < $c; $i++)\n\t\t{\n\t\t\tif (is_string($this->qb_orderby[$i]))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ($this->qb_orderby[$i]['escape'] !== FALSE && ! $this->_is_literal($this->qb_orderby[$i]['field']))\n\t\t\t{\n\t\t\t\t$this->qb_orderby[$i]['field'] = $this->protect_identifiers($this->qb_orderby[$i]['field']);\n\t\t\t}\n\n\t\t\t$this->qb_orderby[$i] = $this->qb_orderby[$i]['field'].$this->qb_orderby[$i]['direction'];\n\t\t}\n\n\t\treturn \"\\nORDER BY \".implode(', ', $this->qb_orderby);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Object to Array\n\t *\n\t * Takes an object as input and converts the class variables to array key/vals\n\t *\n\t * @param\tobject\n\t * @return\tarray\n\t */\n\tprotected function _object_to_array($object)\n\t{\n\t\tif ( ! is_object($object))\n\t\t{\n\t\t\treturn $object;\n\t\t}\n\n\t\t$array = array();\n\t\tforeach (get_object_vars($object) as $key => $val)\n\t\t{\n\t\t\t// There are some built in keys we need to ignore for this conversion\n\t\t\tif ( ! is_object($val) && ! is_array($val) && $key !== '_parent_name')\n\t\t\t{\n\t\t\t\t$array[$key] = $val;\n\t\t\t}\n\t\t}\n\n\t\treturn $array;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Object to Array\n\t *\n\t * Takes an object as input and converts the class variables to array key/vals\n\t *\n\t * @param\tobject\n\t * @return\tarray\n\t */\n\tprotected function _object_to_array_batch($object)\n\t{\n\t\tif ( ! is_object($object))\n\t\t{\n\t\t\treturn $object;\n\t\t}\n\n\t\t$array = array();\n\t\t$out = get_object_vars($object);\n\t\t$fields = array_keys($out);\n\n\t\tforeach ($fields as $val)\n\t\t{\n\t\t\t// There are some built in keys we need to ignore for this conversion\n\t\t\tif ($val !== '_parent_name')\n\t\t\t{\n\t\t\t\t$i = 0;\n\t\t\t\tforeach ($out[$val] as $data)\n\t\t\t\t{\n\t\t\t\t\t$array[$i++][$val] = $data;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $array;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Start Cache\n\t *\n\t * Starts QB caching\n\t *\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function start_cache()\n\t{\n\t\t$this->qb_caching = TRUE;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Stop Cache\n\t *\n\t * Stops QB caching\n\t *\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function stop_cache()\n\t{\n\t\t$this->qb_caching = FALSE;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Flush Cache\n\t *\n\t * Empties the QB cache\n\t *\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function flush_cache()\n\t{\n\t\t$this->_reset_run(array(\n\t\t\t'qb_cache_select'\t\t=> array(),\n\t\t\t'qb_cache_from'\t\t\t=> array(),\n\t\t\t'qb_cache_join'\t\t\t=> array(),\n\t\t\t'qb_cache_where'\t\t=> array(),\n\t\t\t'qb_cache_groupby'\t\t=> array(),\n\t\t\t'qb_cache_having'\t\t=> array(),\n\t\t\t'qb_cache_orderby'\t\t=> array(),\n\t\t\t'qb_cache_set'\t\t\t=> array(),\n\t\t\t'qb_cache_exists'\t\t=> array(),\n\t\t\t'qb_cache_no_escape'\t=> array(),\n\t\t\t'qb_cache_aliased_tables'\t=> array()\n\t\t));\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Merge Cache\n\t *\n\t * When called, this function merges any cached QB arrays with\n\t * locally called ones.\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _merge_cache()\n\t{\n\t\tif (count($this->qb_cache_exists) === 0)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\telseif (in_array('select', $this->qb_cache_exists, TRUE))\n\t\t{\n\t\t\t$qb_no_escape = $this->qb_cache_no_escape;\n\t\t}\n\n\t\tforeach (array_unique($this->qb_cache_exists) as $val) // select, from, etc.\n\t\t{\n\t\t\t$qb_variable\t= 'qb_'.$val;\n\t\t\t$qb_cache_var\t= 'qb_cache_'.$val;\n\t\t\t$qb_new \t= $this->$qb_cache_var;\n\n\t\t\tfor ($i = 0, $c = count($this->$qb_variable); $i < $c; $i++)\n\t\t\t{\n\t\t\t\tif ( ! in_array($this->{$qb_variable}[$i], $qb_new, TRUE))\n\t\t\t\t{\n\t\t\t\t\t$qb_new[] = $this->{$qb_variable}[$i];\n\t\t\t\t\tif ($val === 'select')\n\t\t\t\t\t{\n\t\t\t\t\t\t$qb_no_escape[] = $this->qb_no_escape[$i];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$this->$qb_variable = $qb_new;\n\t\t\tif ($val === 'select')\n\t\t\t{\n\t\t\t\t$this->qb_no_escape = $qb_no_escape;\n\t\t\t}\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Is literal\n\t *\n\t * Determines if a string represents a literal value or a field name\n\t *\n\t * @param\tstring\t$str\n\t * @return\tbool\n\t */\n\tprotected function _is_literal($str)\n\t{\n\t\t$str = trim($str);\n\n\t\tif (empty($str) OR ctype_digit($str) OR (string) (float) $str === $str OR in_array(strtoupper($str), array('TRUE', 'FALSE'), TRUE))\n\t\t{\n\t\t\treturn TRUE;\n\t\t}\n\n\t\tstatic $_str;\n\n\t\tif (empty($_str))\n\t\t{\n\t\t\t$_str = ($this->_escape_char !== '\"')\n\t\t\t\t? array('\"', \"'\") : array(\"'\");\n\t\t}\n\n\t\treturn in_array($str[0], $_str, TRUE);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Reset Query Builder values.\n\t *\n\t * Publicly-visible method to reset the QB values.\n\t *\n\t * @return\tCI_DB_query_builder\n\t */\n\tpublic function reset_query()\n\t{\n\t\t$this->_reset_select();\n\t\t$this->_reset_write();\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Resets the query builder values.  Called by the get() function\n\t *\n\t * @param\tarray\tAn array of fields to reset\n\t * @return\tvoid\n\t */\n\tprotected function _reset_run($qb_reset_items)\n\t{\n\t\tforeach ($qb_reset_items as $item => $default_value)\n\t\t{\n\t\t\t$this->$item = $default_value;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Resets the query builder values.  Called by the get() function\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _reset_select()\n\t{\n\t\t$this->_reset_run(array(\n\t\t\t'qb_select'\t\t=> array(),\n\t\t\t'qb_from'\t\t=> array(),\n\t\t\t'qb_join'\t\t=> array(),\n\t\t\t'qb_where'\t\t=> array(),\n\t\t\t'qb_groupby'\t\t=> array(),\n\t\t\t'qb_having'\t\t=> array(),\n\t\t\t'qb_orderby'\t\t=> array(),\n\t\t\t'qb_aliased_tables'\t=> array(),\n\t\t\t'qb_no_escape'\t\t=> array(),\n\t\t\t'qb_distinct'\t\t=> FALSE,\n\t\t\t'qb_limit'\t\t=> FALSE,\n\t\t\t'qb_offset'\t\t=> FALSE\n\t\t));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Resets the query builder \"write\" values.\n\t *\n\t * Called by the insert() update() insert_batch() update_batch() and delete() functions\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _reset_write()\n\t{\n\t\t$this->_reset_run(array(\n\t\t\t'qb_set'\t=> array(),\n\t\t\t'qb_set_ub'\t=> array(),\n\t\t\t'qb_from'\t=> array(),\n\t\t\t'qb_join'\t=> array(),\n\t\t\t'qb_where'\t=> array(),\n\t\t\t'qb_orderby'\t=> array(),\n\t\t\t'qb_keys'\t=> array(),\n\t\t\t'qb_limit'\t=> FALSE\n\t\t));\n\t}\n\n}\n"
  },
  {
    "path": "system/database/DB_result.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Database Result Class\n *\n * This is the platform-independent result class.\n * This class will not be called directly. Rather, the adapter\n * class for the specific database will extend and instantiate it.\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_result {\n\n\t/**\n\t * Connection ID\n\t *\n\t * @var\tresource|object\n\t */\n\tpublic $conn_id;\n\n\t/**\n\t * Result ID\n\t *\n\t * @var\tresource|object\n\t */\n\tpublic $result_id;\n\n\t/**\n\t * Result Array\n\t *\n\t * @var\tarray[]\n\t */\n\tpublic $result_array\t\t\t= array();\n\n\t/**\n\t * Result Object\n\t *\n\t * @var\tobject[]\n\t */\n\tpublic $result_object\t\t\t= array();\n\n\t/**\n\t * Custom Result Object\n\t *\n\t * @var\tobject[]\n\t */\n\tpublic $custom_result_object\t\t= array();\n\n\t/**\n\t * Current Row index\n\t *\n\t * @var\tint\n\t */\n\tpublic $current_row\t\t\t= 0;\n\n\t/**\n\t * Number of rows\n\t *\n\t * @var\tint\n\t */\n\tpublic $num_rows;\n\n\t/**\n\t * Row data\n\t *\n\t * @var\tarray\n\t */\n\tpublic $row_data;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Constructor\n\t *\n\t * @param\tobject\t$driver_object\n\t * @return\tvoid\n\t */\n\tpublic function __construct(&$driver_object)\n\t{\n\t\t$this->conn_id = $driver_object->conn_id;\n\t\t$this->result_id = $driver_object->result_id;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Number of rows in the result set\n\t *\n\t * @return\tint\n\t */\n\tpublic function num_rows()\n\t{\n\t\tif (is_int($this->num_rows))\n\t\t{\n\t\t\treturn $this->num_rows;\n\t\t}\n\t\telseif (count($this->result_array) > 0)\n\t\t{\n\t\t\treturn $this->num_rows = count($this->result_array);\n\t\t}\n\t\telseif (count($this->result_object) > 0)\n\t\t{\n\t\t\treturn $this->num_rows = count($this->result_object);\n\t\t}\n\n\t\treturn $this->num_rows = count($this->result_array());\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Query result. Acts as a wrapper function for the following functions.\n\t *\n\t * @param\tstring\t$type\t'object', 'array' or a custom class name\n\t * @return\tarray\n\t */\n\tpublic function result($type = 'object')\n\t{\n\t\tif ($type === 'array')\n\t\t{\n\t\t\treturn $this->result_array();\n\t\t}\n\t\telseif ($type === 'object')\n\t\t{\n\t\t\treturn $this->result_object();\n\t\t}\n\n\t\treturn $this->custom_result_object($type);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Custom query result.\n\t *\n\t * @param\tstring\t$class_name\n\t * @return\tarray\n\t */\n\tpublic function custom_result_object($class_name)\n\t{\n\t\tif (isset($this->custom_result_object[$class_name]))\n\t\t{\n\t\t\treturn $this->custom_result_object[$class_name];\n\t\t}\n\t\telseif ( ! $this->result_id OR $this->num_rows === 0)\n\t\t{\n\t\t\treturn array();\n\t\t}\n\n\t\t// Don't fetch the result set again if we already have it\n\t\t$_data = NULL;\n\t\tif (($c = count($this->result_array)) > 0)\n\t\t{\n\t\t\t$_data = 'result_array';\n\t\t}\n\t\telseif (($c = count($this->result_object)) > 0)\n\t\t{\n\t\t\t$_data = 'result_object';\n\t\t}\n\n\t\tif ($_data !== NULL)\n\t\t{\n\t\t\tfor ($i = 0; $i < $c; $i++)\n\t\t\t{\n\t\t\t\t$this->custom_result_object[$class_name][$i] = new $class_name();\n\n\t\t\t\tforeach ($this->{$_data}[$i] as $key => $value)\n\t\t\t\t{\n\t\t\t\t\t$this->custom_result_object[$class_name][$i]->$key = $value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn $this->custom_result_object[$class_name];\n\t\t}\n\n\t\tis_null($this->row_data) OR $this->data_seek(0);\n\t\t$this->custom_result_object[$class_name] = array();\n\n\t\twhile ($row = $this->_fetch_object($class_name))\n\t\t{\n\t\t\t$this->custom_result_object[$class_name][] = $row;\n\t\t}\n\n\t\treturn $this->custom_result_object[$class_name];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Query result. \"object\" version.\n\t *\n\t * @return\tarray\n\t */\n\tpublic function result_object()\n\t{\n\t\tif (count($this->result_object) > 0)\n\t\t{\n\t\t\treturn $this->result_object;\n\t\t}\n\n\t\t// In the event that query caching is on, the result_id variable\n\t\t// will not be a valid resource so we'll simply return an empty\n\t\t// array.\n\t\tif ( ! $this->result_id OR $this->num_rows === 0)\n\t\t{\n\t\t\treturn array();\n\t\t}\n\n\t\tif (($c = count($this->result_array)) > 0)\n\t\t{\n\t\t\tfor ($i = 0; $i < $c; $i++)\n\t\t\t{\n\t\t\t\t$this->result_object[$i] = (object) $this->result_array[$i];\n\t\t\t}\n\n\t\t\treturn $this->result_object;\n\t\t}\n\n\t\tis_null($this->row_data) OR $this->data_seek(0);\n\t\twhile ($row = $this->_fetch_object())\n\t\t{\n\t\t\t$this->result_object[] = $row;\n\t\t}\n\n\t\treturn $this->result_object;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Query result. \"array\" version.\n\t *\n\t * @return\tarray\n\t */\n\tpublic function result_array()\n\t{\n\t\tif (count($this->result_array) > 0)\n\t\t{\n\t\t\treturn $this->result_array;\n\t\t}\n\n\t\t// In the event that query caching is on, the result_id variable\n\t\t// will not be a valid resource so we'll simply return an empty\n\t\t// array.\n\t\tif ( ! $this->result_id OR $this->num_rows === 0)\n\t\t{\n\t\t\treturn array();\n\t\t}\n\n\t\tif (($c = count($this->result_object)) > 0)\n\t\t{\n\t\t\tfor ($i = 0; $i < $c; $i++)\n\t\t\t{\n\t\t\t\t$this->result_array[$i] = (array) $this->result_object[$i];\n\t\t\t}\n\n\t\t\treturn $this->result_array;\n\t\t}\n\n\t\tis_null($this->row_data) OR $this->data_seek(0);\n\t\twhile ($row = $this->_fetch_assoc())\n\t\t{\n\t\t\t$this->result_array[] = $row;\n\t\t}\n\n\t\treturn $this->result_array;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Row\n\t *\n\t * A wrapper method.\n\t *\n\t * @param\tmixed\t$n\n\t * @param\tstring\t$type\t'object' or 'array'\n\t * @return\tmixed\n\t */\n\tpublic function row($n = 0, $type = 'object')\n\t{\n\t\tif ( ! is_numeric($n))\n\t\t{\n\t\t\t// We cache the row data for subsequent uses\n\t\t\tis_array($this->row_data) OR $this->row_data = $this->row_array(0);\n\n\t\t\t// array_key_exists() instead of isset() to allow for NULL values\n\t\t\tif (empty($this->row_data) OR ! array_key_exists($n, $this->row_data))\n\t\t\t{\n\t\t\t\treturn NULL;\n\t\t\t}\n\n\t\t\treturn $this->row_data[$n];\n\t\t}\n\n\t\tif ($type === 'object') return $this->row_object($n);\n\t\telseif ($type === 'array') return $this->row_array($n);\n\n\t\treturn $this->custom_row_object($n, $type);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Assigns an item into a particular column slot\n\t *\n\t * @param\tmixed\t$key\n\t * @param\tmixed\t$value\n\t * @return\tvoid\n\t */\n\tpublic function set_row($key, $value = NULL)\n\t{\n\t\t// We cache the row data for subsequent uses\n\t\tif ( ! is_array($this->row_data))\n\t\t{\n\t\t\t$this->row_data = $this->row_array(0);\n\t\t}\n\n\t\tif (is_array($key))\n\t\t{\n\t\t\tforeach ($key as $k => $v)\n\t\t\t{\n\t\t\t\t$this->row_data[$k] = $v;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif ($key !== '' && $value !== NULL)\n\t\t{\n\t\t\t$this->row_data[$key] = $value;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns a single result row - custom object version\n\t *\n\t * @param\tint\t$n\n\t * @param\tstring\t$type\n\t * @return\tobject\n\t */\n\tpublic function custom_row_object($n, $type)\n\t{\n\t\tisset($this->custom_result_object[$type]) OR $this->custom_result_object[$type] = $this->custom_result_object($type);\n\n\t\tif (count($this->custom_result_object[$type]) === 0)\n\t\t{\n\t\t\treturn NULL;\n\t\t}\n\n\t\tif ($n !== $this->current_row && isset($this->custom_result_object[$type][$n]))\n\t\t{\n\t\t\t$this->current_row = $n;\n\t\t}\n\n\t\treturn $this->custom_result_object[$type][$this->current_row];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns a single result row - object version\n\t *\n\t * @param\tint\t$n\n\t * @return\tobject\n\t */\n\tpublic function row_object($n = 0)\n\t{\n\t\t$result = $this->result_object();\n\t\tif (count($result) === 0)\n\t\t{\n\t\t\treturn NULL;\n\t\t}\n\n\t\tif ($n !== $this->current_row && isset($result[$n]))\n\t\t{\n\t\t\t$this->current_row = $n;\n\t\t}\n\n\t\treturn $result[$this->current_row];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns a single result row - array version\n\t *\n\t * @param\tint\t$n\n\t * @return\tarray\n\t */\n\tpublic function row_array($n = 0)\n\t{\n\t\t$result = $this->result_array();\n\t\tif (count($result) === 0)\n\t\t{\n\t\t\treturn NULL;\n\t\t}\n\n\t\tif ($n !== $this->current_row && isset($result[$n]))\n\t\t{\n\t\t\t$this->current_row = $n;\n\t\t}\n\n\t\treturn $result[$this->current_row];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns the \"first\" row\n\t *\n\t * @param\tstring\t$type\n\t * @return\tmixed\n\t */\n\tpublic function first_row($type = 'object')\n\t{\n\t\t$result = $this->result($type);\n\t\treturn (count($result) === 0) ? NULL : $result[0];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns the \"last\" row\n\t *\n\t * @param\tstring\t$type\n\t * @return\tmixed\n\t */\n\tpublic function last_row($type = 'object')\n\t{\n\t\t$result = $this->result($type);\n\t\treturn (count($result) === 0) ? NULL : $result[count($result) - 1];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns the \"next\" row\n\t *\n\t * @param\tstring\t$type\n\t * @return\tmixed\n\t */\n\tpublic function next_row($type = 'object')\n\t{\n\t\t$result = $this->result($type);\n\t\tif (count($result) === 0)\n\t\t{\n\t\t\treturn NULL;\n\t\t}\n\n\t\treturn isset($result[$this->current_row + 1])\n\t\t\t? $result[++$this->current_row]\n\t\t\t: NULL;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns the \"previous\" row\n\t *\n\t * @param\tstring\t$type\n\t * @return\tmixed\n\t */\n\tpublic function previous_row($type = 'object')\n\t{\n\t\t$result = $this->result($type);\n\t\tif (count($result) === 0)\n\t\t{\n\t\t\treturn NULL;\n\t\t}\n\n\t\tif (isset($result[$this->current_row - 1]))\n\t\t{\n\t\t\t--$this->current_row;\n\t\t}\n\t\treturn $result[$this->current_row];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns an unbuffered row and move pointer to next row\n\t *\n\t * @param\tstring\t$type\t'array', 'object' or a custom class name\n\t * @return\tmixed\n\t */\n\tpublic function unbuffered_row($type = 'object')\n\t{\n\t\tif ($type === 'array')\n\t\t{\n\t\t\treturn $this->_fetch_assoc();\n\t\t}\n\t\telseif ($type === 'object')\n\t\t{\n\t\t\treturn $this->_fetch_object();\n\t\t}\n\n\t\treturn $this->_fetch_object($type);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * The following methods are normally overloaded by the identically named\n\t * methods in the platform-specific driver -- except when query caching\n\t * is used. When caching is enabled we do not load the other driver.\n\t * These functions are primarily here to prevent undefined function errors\n\t * when a cached result object is in use. They are not otherwise fully\n\t * operational due to the unavailability of the database resource IDs with\n\t * cached results.\n\t */\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Number of fields in the result set\n\t *\n\t * Overridden by driver result classes.\n\t *\n\t * @return\tint\n\t */\n\tpublic function num_fields()\n\t{\n\t\treturn 0;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch Field Names\n\t *\n\t * Generates an array of column names.\n\t *\n\t * Overridden by driver result classes.\n\t *\n\t * @return\tarray\n\t */\n\tpublic function list_fields()\n\t{\n\t\treturn array();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field data\n\t *\n\t * Generates an array of objects containing field meta-data.\n\t *\n\t * Overridden by driver result classes.\n\t *\n\t * @return\tarray\n\t */\n\tpublic function field_data()\n\t{\n\t\treturn array();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Free the result\n\t *\n\t * Overridden by driver result classes.\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function free_result()\n\t{\n\t\t$this->result_id = FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Data Seek\n\t *\n\t * Moves the internal pointer to the desired offset. We call\n\t * this internally before fetching results to make sure the\n\t * result set starts at zero.\n\t *\n\t * Overridden by driver result classes.\n\t *\n\t * @param\tint\t$n\n\t * @return\tbool\n\t */\n\tpublic function data_seek($n = 0)\n\t{\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - associative array\n\t *\n\t * Returns the result set as an array.\n\t *\n\t * Overridden by driver result classes.\n\t *\n\t * @return\tarray\n\t */\n\tprotected function _fetch_assoc()\n\t{\n\t\treturn array();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - object\n\t *\n\t * Returns the result set as an object.\n\t *\n\t * Overridden by driver result classes.\n\t *\n\t * @param\tstring\t$class_name\n\t * @return\tobject\n\t */\n\tprotected function _fetch_object($class_name = 'stdClass')\n\t{\n\t\treturn new $class_name();\n\t}\n\n}\n"
  },
  {
    "path": "system/database/DB_utility.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Database Utility Class\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nabstract class CI_DB_utility {\n\n\t/**\n\t * Database object\n\t *\n\t * @var\tobject\n\t */\n\tprotected $db;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * List databases statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_list_databases\t\t= FALSE;\n\n\t/**\n\t * OPTIMIZE TABLE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_optimize_table\t= FALSE;\n\n\t/**\n\t * REPAIR TABLE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_repair_table\t= FALSE;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * @param\tobject\t&$db\tDatabase object\n\t * @return\tvoid\n\t */\n\tpublic function __construct(&$db)\n\t{\n\t\t$this->db =& $db;\n\t\tlog_message('info', 'Database Utility Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * List databases\n\t *\n\t * @return\tarray\n\t */\n\tpublic function list_databases()\n\t{\n\t\t// Is there a cached result?\n\t\tif (isset($this->db->data_cache['db_names']))\n\t\t{\n\t\t\treturn $this->db->data_cache['db_names'];\n\t\t}\n\t\telseif ($this->_list_databases === FALSE)\n\t\t{\n\t\t\treturn ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;\n\t\t}\n\n\t\t$this->db->data_cache['db_names'] = array();\n\n\t\t$query = $this->db->query($this->_list_databases);\n\t\tif ($query === FALSE)\n\t\t{\n\t\t\treturn $this->db->data_cache['db_names'];\n\t\t}\n\n\t\tfor ($i = 0, $query = $query->result_array(), $c = count($query); $i < $c; $i++)\n\t\t{\n\t\t\t$this->db->data_cache['db_names'][] = current($query[$i]);\n\t\t}\n\n\t\treturn $this->db->data_cache['db_names'];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Determine if a particular database exists\n\t *\n\t * @param\tstring\t$database_name\n\t * @return\tbool\n\t */\n\tpublic function database_exists($database_name)\n\t{\n\t\treturn in_array($database_name, $this->list_databases());\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Optimize Table\n\t *\n\t * @param\tstring\t$table_name\n\t * @return\tmixed\n\t */\n\tpublic function optimize_table($table_name)\n\t{\n\t\tif ($this->_optimize_table === FALSE)\n\t\t{\n\t\t\treturn ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;\n\t\t}\n\n\t\t$query = $this->db->query(sprintf($this->_optimize_table, $this->db->escape_identifiers($table_name)));\n\t\tif ($query !== FALSE)\n\t\t{\n\t\t\t$query = $query->result_array();\n\t\t\treturn current($query);\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Optimize Database\n\t *\n\t * @return\tmixed\n\t */\n\tpublic function optimize_database()\n\t{\n\t\tif ($this->_optimize_table === FALSE)\n\t\t{\n\t\t\treturn ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;\n\t\t}\n\n\t\t$result = array();\n\t\tforeach ($this->db->list_tables() as $table_name)\n\t\t{\n\t\t\t$res = $this->db->query(sprintf($this->_optimize_table, $this->db->escape_identifiers($table_name)));\n\t\t\tif (is_bool($res))\n\t\t\t{\n\t\t\t\treturn $res;\n\t\t\t}\n\n\t\t\t// Build the result array...\n\t\t\t$res = $res->result_array();\n\t\t\t$res = current($res);\n\t\t\t$key = str_replace($this->db->database.'.', '', current($res));\n\t\t\t$keys = array_keys($res);\n\t\t\tunset($res[$keys[0]]);\n\n\t\t\t$result[$key] = $res;\n\t\t}\n\n\t\treturn $result;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Repair Table\n\t *\n\t * @param\tstring\t$table_name\n\t * @return\tmixed\n\t */\n\tpublic function repair_table($table_name)\n\t{\n\t\tif ($this->_repair_table === FALSE)\n\t\t{\n\t\t\treturn ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;\n\t\t}\n\n\t\t$query = $this->db->query(sprintf($this->_repair_table, $this->db->escape_identifiers($table_name)));\n\t\tif (is_bool($query))\n\t\t{\n\t\t\treturn $query;\n\t\t}\n\n\t\t$query = $query->result_array();\n\t\treturn current($query);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Generate CSV from a query result object\n\t *\n\t * @param\tobject\t$query\t\tQuery result object\n\t * @param\tstring\t$delim\t\tDelimiter (default: ,)\n\t * @param\tstring\t$newline\tNewline character (default: \\n)\n\t * @param\tstring\t$enclosure\tEnclosure (default: \")\n\t * @return\tstring\n\t */\n\tpublic function csv_from_result(CI_DB_result $query, $delim = ',', $newline = \"\\n\", $enclosure = '\"')\n\t{\n\t\t$out = '';\n\t\t// First generate the headings from the table column names\n\t\tforeach ($query->list_fields() as $name)\n\t\t{\n\t\t\t$out .= $enclosure.str_replace($enclosure, $enclosure.$enclosure, $name).$enclosure.$delim;\n\t\t}\n\n\t\t$out = substr($out, 0, -strlen($delim)).$newline;\n\n\t\t// Next blast through the result array and build out the rows\n\t\twhile ($row = $query->unbuffered_row('array'))\n\t\t{\n\t\t\t$line = array();\n\t\t\tforeach ($row as $item)\n\t\t\t{\n\t\t\t\t$line[] = $enclosure.str_replace($enclosure, $enclosure.$enclosure, (string) $item).$enclosure;\n\t\t\t}\n\t\t\t$out .= implode($delim, $line).$newline;\n\t\t}\n\n\t\treturn $out;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Generate XML data from a query result object\n\t *\n\t * @param\tobject\t$query\tQuery result object\n\t * @param\tarray\t$params\tAny preferences\n\t * @return\tstring\n\t */\n\tpublic function xml_from_result(CI_DB_result $query, $params = array())\n\t{\n\t\t// Set our default values\n\t\tforeach (array('root' => 'root', 'element' => 'element', 'newline' => \"\\n\", 'tab' => \"\\t\") as $key => $val)\n\t\t{\n\t\t\tif ( ! isset($params[$key]))\n\t\t\t{\n\t\t\t\t$params[$key] = $val;\n\t\t\t}\n\t\t}\n\n\t\t// Create variables for convenience\n\t\textract($params);\n\n\t\t// Load the xml helper\n\t\tget_instance()->load->helper('xml');\n\n\t\t// Generate the result\n\t\t$xml = '<'.$root.'>'.$newline;\n\t\twhile ($row = $query->unbuffered_row())\n\t\t{\n\t\t\t$xml .= $tab.'<'.$element.'>'.$newline;\n\t\t\tforeach ($row as $key => $val)\n\t\t\t{\n\t\t\t\t$xml .= $tab.$tab.'<'.$key.'>'.xml_convert($val).'</'.$key.'>'.$newline;\n\t\t\t}\n\t\t\t$xml .= $tab.'</'.$element.'>'.$newline;\n\t\t}\n\n\t\treturn $xml.'</'.$root.'>'.$newline;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Database Backup\n\t *\n\t * @param\tarray\t$params\n\t * @return\tstring\n\t */\n\tpublic function backup($params = array())\n\t{\n\t\t// If the parameters have not been submitted as an\n\t\t// array then we know that it is simply the table\n\t\t// name, which is a valid short cut.\n\t\tif (is_string($params))\n\t\t{\n\t\t\t$params = array('tables' => $params);\n\t\t}\n\n\t\t// Set up our default preferences\n\t\t$prefs = array(\n\t\t\t'tables'\t\t=> array(),\n\t\t\t'ignore'\t\t=> array(),\n\t\t\t'filename'\t\t=> '',\n\t\t\t'format'\t\t=> 'gzip', // gzip, zip, txt\n\t\t\t'add_drop'\t\t=> TRUE,\n\t\t\t'add_insert'\t\t=> TRUE,\n\t\t\t'newline'\t\t=> \"\\n\",\n\t\t\t'foreign_key_checks'\t=> TRUE\n\t\t);\n\n\t\t// Did the user submit any preferences? If so set them....\n\t\tif (count($params) > 0)\n\t\t{\n\t\t\tforeach ($prefs as $key => $val)\n\t\t\t{\n\t\t\t\tif (isset($params[$key]))\n\t\t\t\t{\n\t\t\t\t\t$prefs[$key] = $params[$key];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Are we backing up a complete database or individual tables?\n\t\t// If no table names were submitted we'll fetch the entire table list\n\t\tif (count($prefs['tables']) === 0)\n\t\t{\n\t\t\t$prefs['tables'] = $this->db->list_tables();\n\t\t}\n\n\t\t// Validate the format\n\t\tif ( ! in_array($prefs['format'], array('gzip', 'zip', 'txt'), TRUE))\n\t\t{\n\t\t\t$prefs['format'] = 'txt';\n\t\t}\n\n\t\t// Is the encoder supported? If not, we'll either issue an\n\t\t// error or use plain text depending on the debug settings\n\t\tif (($prefs['format'] === 'gzip' && ! function_exists('gzencode'))\n\t\t\tOR ($prefs['format'] === 'zip' && ! function_exists('gzcompress')))\n\t\t{\n\t\t\tif ($this->db->db_debug)\n\t\t\t{\n\t\t\t\treturn $this->db->display_error('db_unsupported_compression');\n\t\t\t}\n\n\t\t\t$prefs['format'] = 'txt';\n\t\t}\n\n\t\t// Was a Zip file requested?\n\t\tif ($prefs['format'] === 'zip')\n\t\t{\n\t\t\t// Set the filename if not provided (only needed with Zip files)\n\t\t\tif ($prefs['filename'] === '')\n\t\t\t{\n\t\t\t\t$prefs['filename'] = (count($prefs['tables']) === 1 ? $prefs['tables'] : $this->db->database)\n\t\t\t\t\t\t\t.date('Y-m-d_H-i', time()).'.sql';\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// If they included the .zip file extension we'll remove it\n\t\t\t\tif (preg_match('|.+?\\.zip$|', $prefs['filename']))\n\t\t\t\t{\n\t\t\t\t\t$prefs['filename'] = str_replace('.zip', '', $prefs['filename']);\n\t\t\t\t}\n\n\t\t\t\t// Tack on the \".sql\" file extension if needed\n\t\t\t\tif ( ! preg_match('|.+?\\.sql$|', $prefs['filename']))\n\t\t\t\t{\n\t\t\t\t\t$prefs['filename'] .= '.sql';\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Load the Zip class and output it\n\t\t\t$CI =& get_instance();\n\t\t\t$CI->load->library('zip');\n\t\t\t$CI->zip->add_data($prefs['filename'], $this->_backup($prefs));\n\t\t\treturn $CI->zip->get_zip();\n\t\t}\n\t\telseif ($prefs['format'] === 'txt') // Was a text file requested?\n\t\t{\n\t\t\treturn $this->_backup($prefs);\n\t\t}\n\t\telseif ($prefs['format'] === 'gzip') // Was a Gzip file requested?\n\t\t{\n\t\t\treturn gzencode($this->_backup($prefs));\n\t\t}\n\n\t\treturn;\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/cubrid/cubrid_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 2.1.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CUBRID Database Adapter Class\n *\n * Note: _DB is an extender class that the app controller\n * creates dynamically based on whether the query builder\n * class is being used or not.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEsen Sagynov\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_cubrid_driver extends CI_DB {\n\n\t/**\n\t * Database driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $dbdriver = 'cubrid';\n\n\t/**\n\t * Auto-commit flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $auto_commit = TRUE;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Identifier escape character\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_escape_char = '`';\n\n\t/**\n\t * ORDER BY random keyword\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_random_keyword = array('RANDOM()', 'RANDOM(%d)');\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * @param\tarray\t$params\n\t * @return\tvoid\n\t */\n\tpublic function __construct($params)\n\t{\n\t\tparent::__construct($params);\n\n\t\tif (preg_match('/^CUBRID:[^:]+(:[0-9][1-9]{0,4})?:[^:]+:[^:]*:[^:]*:(\\?.+)?$/', $this->dsn, $matches))\n\t\t{\n\t\t\tif (stripos($matches[2], 'autocommit=off') !== FALSE)\n\t\t\t{\n\t\t\t\t$this->auto_commit = FALSE;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// If no port is defined by the user, use the default value\n\t\t\tempty($this->port) OR $this->port = 33000;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Non-persistent database connection\n\t *\n\t * @param\tbool\t$persistent\n\t * @return\tresource\n\t */\n\tpublic function db_connect($persistent = FALSE)\n\t{\n\t\tif (preg_match('/^CUBRID:[^:]+(:[0-9][1-9]{0,4})?:[^:]+:([^:]*):([^:]*):(\\?.+)?$/', $this->dsn, $matches))\n\t\t{\n\t\t\t$func = ($persistent !== TRUE) ? 'cubrid_connect_with_url' : 'cubrid_pconnect_with_url';\n\t\t\treturn ($matches[2] === '' && $matches[3] === '' && $this->username !== '' && $this->password !== '')\n\t\t\t\t? $func($this->dsn, $this->username, $this->password)\n\t\t\t\t: $func($this->dsn);\n\t\t}\n\n\t\t$func = ($persistent !== TRUE) ? 'cubrid_connect' : 'cubrid_pconnect';\n\t\treturn ($this->username !== '')\n\t\t\t? $func($this->hostname, $this->port, $this->database, $this->username, $this->password)\n\t\t\t: $func($this->hostname, $this->port, $this->database);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Reconnect\n\t *\n\t * Keep / reestablish the db connection if no queries have been\n\t * sent for a length of time exceeding the server's idle timeout\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function reconnect()\n\t{\n\t\tif (cubrid_ping($this->conn_id) === FALSE)\n\t\t{\n\t\t\t$this->conn_id = FALSE;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Database version number\n\t *\n\t * @return\tstring\n\t */\n\tpublic function version()\n\t{\n\t\tif (isset($this->data_cache['version']))\n\t\t{\n\t\t\treturn $this->data_cache['version'];\n\t\t}\n\n\t\treturn ( ! $this->conn_id OR ($version = cubrid_get_server_info($this->conn_id)) === FALSE)\n\t\t\t? FALSE\n\t\t\t: $this->data_cache['version'] = $version;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Execute the query\n\t *\n\t * @param\tstring\t$sql\tan SQL query\n\t * @return\tresource\n\t */\n\tprotected function _execute($sql)\n\t{\n\t\treturn cubrid_query($sql, $this->conn_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Begin Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_begin()\n\t{\n\t\tif (($autocommit = cubrid_get_autocommit($this->conn_id)) === NULL)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\t\telseif ($autocommit === TRUE)\n\t\t{\n\t\t\treturn cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_FALSE);\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Commit Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_commit()\n\t{\n\t\tif ( ! cubrid_commit($this->conn_id))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif ($this->auto_commit && ! cubrid_get_autocommit($this->conn_id))\n\t\t{\n\t\t\treturn cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_TRUE);\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Rollback Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_rollback()\n\t{\n\t\tif ( ! cubrid_rollback($this->conn_id))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif ($this->auto_commit && ! cubrid_get_autocommit($this->conn_id))\n\t\t{\n\t\t\tcubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_TRUE);\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Platform-dependent string escape\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tprotected function _escape_str($str)\n\t{\n\t\treturn cubrid_real_escape_string($str, $this->conn_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Affected Rows\n\t *\n\t * @return\tint\n\t */\n\tpublic function affected_rows()\n\t{\n\t\treturn cubrid_affected_rows();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert ID\n\t *\n\t * @return\tint\n\t */\n\tpublic function insert_id()\n\t{\n\t\treturn cubrid_insert_id($this->conn_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * List table query\n\t *\n\t * Generates a platform-specific query string so that the table names can be fetched\n\t *\n\t * @param\tbool\t$prefix_limit\n\t * @return\tstring\n\t */\n\tprotected function _list_tables($prefix_limit = FALSE)\n\t{\n\t\t$sql = 'SHOW TABLES';\n\n\t\tif ($prefix_limit !== FALSE && $this->dbprefix !== '')\n\t\t{\n\t\t\treturn $sql.\" LIKE '\".$this->escape_like_str($this->dbprefix).\"%'\";\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show column query\n\t *\n\t * Generates a platform-specific query string so that the column names can be fetched\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _list_columns($table = '')\n\t{\n\t\treturn 'SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns an object with field data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tarray\n\t */\n\tpublic function field_data($table)\n\t{\n\t\tif (($query = $this->query('SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE))) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\t\t$query = $query->result_object();\n\n\t\t$retval = array();\n\t\tfor ($i = 0, $c = count($query); $i < $c; $i++)\n\t\t{\n\t\t\t$retval[$i]\t\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t\t= $query[$i]->Field;\n\n\t\t\tsscanf($query[$i]->Type, '%[a-z](%d)',\n\t\t\t\t$retval[$i]->type,\n\t\t\t\t$retval[$i]->max_length\n\t\t\t);\n\n\t\t\t$retval[$i]->default\t\t= $query[$i]->Default;\n\t\t\t$retval[$i]->primary_key\t= (int) ($query[$i]->Key === 'PRI');\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Error\n\t *\n\t * Returns an array containing code and message of the last\n\t * database error that has occurred.\n\t *\n\t * @return\tarray\n\t */\n\tpublic function error()\n\t{\n\t\treturn array('code' => cubrid_errno($this->conn_id), 'message' => cubrid_error($this->conn_id));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * FROM tables\n\t *\n\t * Groups tables in FROM clauses if needed, so there is no confusion\n\t * about operator precedence.\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _from_tables()\n\t{\n\t\tif ( ! empty($this->qb_join) && count($this->qb_from) > 1)\n\t\t{\n\t\t\treturn '('.implode(', ', $this->qb_from).')';\n\t\t}\n\n\t\treturn implode(', ', $this->qb_from);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Close DB Connection\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _close()\n\t{\n\t\tcubrid_close($this->conn_id);\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/cubrid/cubrid_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 2.1.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CUBRID Forge Class\n *\n * @category\tDatabase\n * @author\t\tEsen Sagynov\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_cubrid_forge extends CI_DB_forge {\n\n\t/**\n\t * CREATE DATABASE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_database\t= FALSE;\n\n\t/**\n\t * CREATE TABLE keys flag\n\t *\n\t * Whether table keys are created from within the\n\t * CREATE TABLE statement.\n\t *\n\t * @var\tbool\n\t */\n\tprotected $_create_table_keys\t= TRUE;\n\n\t/**\n\t * DROP DATABASE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_drop_database\t= FALSE;\n\n\t/**\n\t * CREATE TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_table_if\t= FALSE;\n\n\t/**\n\t * UNSIGNED support\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_unsigned\t\t= array(\n\t\t'SHORT'\t\t=> 'INTEGER',\n\t\t'SMALLINT'\t=> 'INTEGER',\n\t\t'INT'\t\t=> 'BIGINT',\n\t\t'INTEGER'\t=> 'BIGINT',\n\t\t'BIGINT'\t=> 'NUMERIC',\n\t\t'FLOAT'\t\t=> 'DOUBLE',\n\t\t'REAL'\t\t=> 'DOUBLE'\n\t);\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ALTER TABLE\n\t *\n\t * @param\tstring\t$alter_type\tALTER type\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tmixed\t$field\t\tColumn definition\n\t * @return\tstring|string[]\n\t */\n\tprotected function _alter_table($alter_type, $table, $field)\n\t{\n\t\tif (in_array($alter_type, array('DROP', 'ADD'), TRUE))\n\t\t{\n\t\t\treturn parent::_alter_table($alter_type, $table, $field);\n\t\t}\n\n\t\t$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table);\n\t\t$sqls = array();\n\t\tfor ($i = 0, $c = count($field); $i < $c; $i++)\n\t\t{\n\t\t\tif ($field[$i]['_literal'] !== FALSE)\n\t\t\t{\n\t\t\t\t$sqls[] = $sql.' CHANGE '.$field[$i]['_literal'];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$alter_type = empty($field[$i]['new_name']) ? ' MODIFY ' : ' CHANGE ';\n\t\t\t\t$sqls[] = $sql.$alter_type.$this->_process_column($field[$i]);\n\t\t\t}\n\t\t}\n\n\t\treturn $sqls;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Process column\n\t *\n\t * @param\tarray\t$field\n\t * @return\tstring\n\t */\n\tprotected function _process_column($field)\n\t{\n\t\t$extra_clause = isset($field['after'])\n\t\t\t? ' AFTER '.$this->db->escape_identifiers($field['after']) : '';\n\n\t\tif (empty($extra_clause) && isset($field['first']) && $field['first'] === TRUE)\n\t\t{\n\t\t\t$extra_clause = ' FIRST';\n\t\t}\n\n\t\treturn $this->db->escape_identifiers($field['name'])\n\t\t\t.(empty($field['new_name']) ? '' : ' '.$this->db->escape_identifiers($field['new_name']))\n\t\t\t.' '.$field['type'].$field['length']\n\t\t\t.$field['unsigned']\n\t\t\t.$field['null']\n\t\t\t.$field['default']\n\t\t\t.$field['auto_increment']\n\t\t\t.$field['unique']\n\t\t\t.$extra_clause;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute TYPE\n\t *\n\t * Performs a data type mapping between different databases.\n\t *\n\t * @param\tarray\t&$attributes\n\t * @return\tvoid\n\t */\n\tprotected function _attr_type(&$attributes)\n\t{\n\t\tswitch (strtoupper($attributes['TYPE']))\n\t\t{\n\t\t\tcase 'TINYINT':\n\t\t\t\t$attributes['TYPE'] = 'SMALLINT';\n\t\t\t\t$attributes['UNSIGNED'] = FALSE;\n\t\t\t\treturn;\n\t\t\tcase 'MEDIUMINT':\n\t\t\t\t$attributes['TYPE'] = 'INTEGER';\n\t\t\t\t$attributes['UNSIGNED'] = FALSE;\n\t\t\t\treturn;\n\t\t\tcase 'LONGTEXT':\n\t\t\t\t$attributes['TYPE'] = 'STRING';\n\t\t\t\treturn;\n\t\t\tdefault: return;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Process indexes\n\t *\n\t * @param\tstring\t$table\t(ignored)\n\t * @return\tstring\n\t */\n\tprotected function _process_indexes($table)\n\t{\n\t\t$sql = '';\n\n\t\tfor ($i = 0, $c = count($this->keys); $i < $c; $i++)\n\t\t{\n\t\t\tif (is_array($this->keys[$i]))\n\t\t\t{\n\t\t\t\tfor ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++)\n\t\t\t\t{\n\t\t\t\t\tif ( ! isset($this->fields[$this->keys[$i][$i2]]))\n\t\t\t\t\t{\n\t\t\t\t\t\tunset($this->keys[$i][$i2]);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telseif ( ! isset($this->fields[$this->keys[$i]]))\n\t\t\t{\n\t\t\t\tunset($this->keys[$i]);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tis_array($this->keys[$i]) OR $this->keys[$i] = array($this->keys[$i]);\n\n\t\t\t$sql .= \",\\n\\tKEY \".$this->db->escape_identifiers(implode('_', $this->keys[$i]))\n\t\t\t\t.' ('.implode(', ', $this->db->escape_identifiers($this->keys[$i])).')';\n\t\t}\n\n\t\t$this->keys = array();\n\n\t\treturn $sql;\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/cubrid/cubrid_result.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 2.1.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CUBRID Result Class\n *\n * This class extends the parent result class: CI_DB_result\n *\n * @category\tDatabase\n * @author\t\tEsen Sagynov\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_cubrid_result extends CI_DB_result {\n\n\t/**\n\t * Number of rows in the result set\n\t *\n\t * @return\tint\n\t */\n\tpublic function num_rows()\n\t{\n\t\treturn is_int($this->num_rows)\n\t\t\t? $this->num_rows\n\t\t\t: $this->num_rows = cubrid_num_rows($this->result_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Number of fields in the result set\n\t *\n\t * @return\tint\n\t */\n\tpublic function num_fields()\n\t{\n\t\treturn cubrid_num_fields($this->result_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch Field Names\n\t *\n\t * Generates an array of column names\n\t *\n\t * @return\tarray\n\t */\n\tpublic function list_fields()\n\t{\n\t\treturn cubrid_column_names($this->result_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field data\n\t *\n\t * Generates an array of objects containing field meta-data\n\t *\n\t * @return\tarray\n\t */\n\tpublic function field_data()\n\t{\n\t\t$retval = array();\n\n\t\tfor ($i = 0, $c = $this->num_fields(); $i < $c; $i++)\n\t\t{\n\t\t\t$retval[$i]\t\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t\t= cubrid_field_name($this->result_id, $i);\n\t\t\t$retval[$i]->type\t\t= cubrid_field_type($this->result_id, $i);\n\t\t\t$retval[$i]->max_length\t\t= cubrid_field_len($this->result_id, $i);\n\t\t\t$retval[$i]->primary_key\t= (int) (strpos(cubrid_field_flags($this->result_id, $i), 'primary_key') !== FALSE);\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Free the result\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function free_result()\n\t{\n\t\tif (is_resource($this->result_id) OR\n\t\t\t(get_resource_type($this->result_id) === 'Unknown' && preg_match('/Resource id #/', strval($this->result_id))))\n\t\t{\n\t\t\tcubrid_close_request($this->result_id);\n\t\t\t$this->result_id = FALSE;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Data Seek\n\t *\n\t * Moves the internal pointer to the desired offset. We call\n\t * this internally before fetching results to make sure the\n\t * result set starts at zero.\n\t *\n\t * @param\tint\t$n\n\t * @return\tbool\n\t */\n\tpublic function data_seek($n = 0)\n\t{\n\t\treturn cubrid_data_seek($this->result_id, $n);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - associative array\n\t *\n\t * Returns the result set as an array\n\t *\n\t * @return\tarray\n\t */\n\tprotected function _fetch_assoc()\n\t{\n\t\treturn cubrid_fetch_assoc($this->result_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - object\n\t *\n\t * Returns the result set as an object\n\t *\n\t * @param\tstring\t$class_name\n\t * @return\tobject\n\t */\n\tprotected function _fetch_object($class_name = 'stdClass')\n\t{\n\t\treturn cubrid_fetch_object($this->result_id, $class_name);\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/cubrid/cubrid_utility.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 2.1.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CUBRID Utility Class\n *\n * @category\tDatabase\n * @author\t\tEsen Sagynov\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_cubrid_utility extends CI_DB_utility {\n\n\t/**\n\t * List databases\n\t *\n\t * @return\tarray\n\t */\n\tpublic function list_databases()\n\t{\n\t\tif (isset($this->db->data_cache['db_names']))\n\t\t{\n\t\t\treturn $this->db->data_cache['db_names'];\n\t\t}\n\n\t\treturn $this->db->data_cache['db_names'] = cubrid_list_dbs($this->db->conn_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * CUBRID Export\n\t *\n\t * @param\tarray\tPreferences\n\t * @return\tmixed\n\t */\n\tprotected function _backup($params = array())\n\t{\n\t\t// No SQL based support in CUBRID as of version 8.4.0. Database or\n\t\t// table backup can be performed using CUBRID Manager\n\t\t// database administration tool.\n\t\treturn $this->db->display_error('db_unsupported_feature');\n\t}\n}\n"
  },
  {
    "path": "system/database/drivers/cubrid/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/database/drivers/ibase/ibase_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Firebird/Interbase Database Adapter Class\n *\n * Note: _DB is an extender class that the app controller\n * creates dynamically based on whether the query builder\n * class is being used or not.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_ibase_driver extends CI_DB {\n\n\t/**\n\t * Database driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $dbdriver = 'ibase';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ORDER BY random keyword\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_random_keyword = array('RAND()', 'RAND()');\n\n\t/**\n\t * IBase Transaction status flag\n\t *\n\t * @var\tresource\n\t */\n\tprotected $_ibase_trans;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Non-persistent database connection\n\t *\n\t * @param\tbool\t$persistent\n\t * @return\tresource\n\t */\n\tpublic function db_connect($persistent = FALSE)\n\t{\n\t\treturn ($persistent === TRUE)\n\t\t\t? ibase_pconnect($this->hostname.':'.$this->database, $this->username, $this->password, $this->char_set)\n\t\t\t: ibase_connect($this->hostname.':'.$this->database, $this->username, $this->password, $this->char_set);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Database version number\n\t *\n\t * @return\tstring\n\t */\n\tpublic function version()\n\t{\n\t\tif (isset($this->data_cache['version']))\n\t\t{\n\t\t\treturn $this->data_cache['version'];\n\t\t}\n\n\t\tif (($service = ibase_service_attach($this->hostname, $this->username, $this->password)))\n\t\t{\n\t\t\t$this->data_cache['version'] = ibase_server_info($service, IBASE_SVC_SERVER_VERSION);\n\n\t\t\t// Don't keep the service open\n\t\t\tibase_service_detach($service);\n\t\t\treturn $this->data_cache['version'];\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Execute the query\n\t *\n\t * @param\tstring\t$sql\tan SQL query\n\t * @return\tresource\n\t */\n\tprotected function _execute($sql)\n\t{\n\t\treturn ibase_query(isset($this->_ibase_trans) ? $this->_ibase_trans : $this->conn_id, $sql);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Begin Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_begin()\n\t{\n\t\tif (($trans_handle = ibase_trans($this->conn_id)) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$this->_ibase_trans = $trans_handle;\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Commit Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_commit()\n\t{\n\t\tif (ibase_commit($this->_ibase_trans))\n\t\t{\n\t\t\t$this->_ibase_trans = NULL;\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Rollback Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_rollback()\n\t{\n\t\tif (ibase_rollback($this->_ibase_trans))\n\t\t{\n\t\t\t$this->_ibase_trans = NULL;\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Affected Rows\n\t *\n\t * @return\tint\n\t */\n\tpublic function affected_rows()\n\t{\n\t\treturn ibase_affected_rows($this->conn_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert ID\n\t *\n\t * @param\tstring\t$generator_name\n\t * @param\tint\t$inc_by\n\t * @return\tint\n\t */\n\tpublic function insert_id($generator_name, $inc_by = 0)\n\t{\n\t\t//If a generator hasn't been used before it will return 0\n\t\treturn ibase_gen_id('\"'.$generator_name.'\"', $inc_by);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * List table query\n\t *\n\t * Generates a platform-specific query string so that the table names can be fetched\n\t *\n\t * @param\tbool\t$prefix_limit\n\t * @return\tstring\n\t */\n\tprotected function _list_tables($prefix_limit = FALSE)\n\t{\n\t\t$sql = 'SELECT TRIM(\"RDB$RELATION_NAME\") AS TABLE_NAME FROM \"RDB$RELATIONS\" WHERE \"RDB$RELATION_NAME\" NOT LIKE \\'RDB$%\\' AND \"RDB$RELATION_NAME\" NOT LIKE \\'MON$%\\'';\n\n\t\tif ($prefix_limit !== FALSE && $this->dbprefix !== '')\n\t\t{\n\t\t\treturn $sql.' AND TRIM(\"RDB$RELATION_NAME\") AS TABLE_NAME LIKE \\''.$this->escape_like_str($this->dbprefix).\"%' \"\n\t\t\t\t.sprintf($this->_like_escape_str, $this->_like_escape_chr);\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show column query\n\t *\n\t * Generates a platform-specific query string so that the column names can be fetched\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _list_columns($table = '')\n\t{\n\t\treturn 'SELECT TRIM(\"RDB$FIELD_NAME\") AS COLUMN_NAME FROM \"RDB$RELATION_FIELDS\" WHERE \"RDB$RELATION_NAME\" = '.$this->escape($table);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns an object with field data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tarray\n\t */\n\tpublic function field_data($table)\n\t{\n\t\t$sql = 'SELECT \"rfields\".\"RDB$FIELD_NAME\" AS \"name\",\n\t\t\t\tCASE \"fields\".\"RDB$FIELD_TYPE\"\n\t\t\t\t\tWHEN 7 THEN \\'SMALLINT\\'\n\t\t\t\t\tWHEN 8 THEN \\'INTEGER\\'\n\t\t\t\t\tWHEN 9 THEN \\'QUAD\\'\n\t\t\t\t\tWHEN 10 THEN \\'FLOAT\\'\n\t\t\t\t\tWHEN 11 THEN \\'DFLOAT\\'\n\t\t\t\t\tWHEN 12 THEN \\'DATE\\'\n\t\t\t\t\tWHEN 13 THEN \\'TIME\\'\n\t\t\t\t\tWHEN 14 THEN \\'CHAR\\'\n\t\t\t\t\tWHEN 16 THEN \\'INT64\\'\n\t\t\t\t\tWHEN 27 THEN \\'DOUBLE\\'\n\t\t\t\t\tWHEN 35 THEN \\'TIMESTAMP\\'\n\t\t\t\t\tWHEN 37 THEN \\'VARCHAR\\'\n\t\t\t\t\tWHEN 40 THEN \\'CSTRING\\'\n\t\t\t\t\tWHEN 261 THEN \\'BLOB\\'\n\t\t\t\t\tELSE NULL\n\t\t\t\tEND AS \"type\",\n\t\t\t\t\"fields\".\"RDB$FIELD_LENGTH\" AS \"max_length\",\n\t\t\t\t\"rfields\".\"RDB$DEFAULT_VALUE\" AS \"default\"\n\t\t\tFROM \"RDB$RELATION_FIELDS\" \"rfields\"\n\t\t\t\tJOIN \"RDB$FIELDS\" \"fields\" ON \"rfields\".\"RDB$FIELD_SOURCE\" = \"fields\".\"RDB$FIELD_NAME\"\n\t\t\tWHERE \"rfields\".\"RDB$RELATION_NAME\" = '.$this->escape($table).'\n\t\t\tORDER BY \"rfields\".\"RDB$FIELD_POSITION\"';\n\n\t\treturn (($query = $this->query($sql)) !== FALSE)\n\t\t\t? $query->result_object()\n\t\t\t: FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Error\n\t *\n\t * Returns an array containing code and message of the last\n\t * database error that has occurred.\n\t *\n\t * @return\tarray\n\t */\n\tpublic function error()\n\t{\n\t\treturn array('code' => ibase_errcode(), 'message' => ibase_errmsg());\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Update statement\n\t *\n\t * Generates a platform-specific update string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @param\tarray\t$values\n\t * @return\tstring\n\t */\n\tprotected function _update($table, $values)\n\t{\n\t\t$this->qb_limit = FALSE;\n\t\treturn parent::_update($table, $values);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Truncate statement\n\t *\n\t * Generates a platform-specific truncate string from the supplied data\n\t *\n\t * If the database does not support the TRUNCATE statement,\n\t * then this method maps to 'DELETE FROM table'\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _truncate($table)\n\t{\n\t\treturn 'DELETE FROM '.$table;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Delete statement\n\t *\n\t * Generates a platform-specific delete string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _delete($table)\n\t{\n\t\t$this->qb_limit = FALSE;\n\t\treturn parent::_delete($table);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * LIMIT\n\t *\n\t * Generates a platform-specific LIMIT clause\n\t *\n\t * @param\tstring\t$sql\tSQL Query\n\t * @return\tstring\n\t */\n\tprotected function _limit($sql)\n\t{\n\t\t// Limit clause depends on if Interbase or Firebird\n\t\tif (stripos($this->version(), 'firebird') !== FALSE)\n\t\t{\n\t\t\t$select = 'FIRST '.$this->qb_limit\n\t\t\t\t.($this->qb_offset ? ' SKIP '.$this->qb_offset : '');\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$select = 'ROWS '\n\t\t\t\t.($this->qb_offset ? $this->qb_offset.' TO '.($this->qb_limit + $this->qb_offset) : $this->qb_limit);\n\t\t}\n\n\t\treturn preg_replace('`SELECT`i', 'SELECT '.$select, $sql, 1);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert batch statement\n\t *\n\t * Generates a platform-specific insert string from the supplied data.\n\t *\n\t * @param\tstring\t$table\tTable name\n\t * @param\tarray\t$keys\tINSERT keys\n\t * @param\tarray\t$values\tINSERT values\n\t * @return\tstring|bool\n\t */\n\tprotected function _insert_batch($table, $keys, $values)\n\t{\n\t\treturn ($this->db_debug) ? $this->display_error('db_unsupported_feature') : FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Close DB Connection\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _close()\n\t{\n\t\tibase_close($this->conn_id);\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/ibase/ibase_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Interbase/Firebird Forge Class\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_ibase_forge extends CI_DB_forge {\n\n\t/**\n\t * CREATE TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_table_if\t= FALSE;\n\n\t/**\n\t * RENAME TABLE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_rename_table\t= FALSE;\n\n\t/**\n\t * DROP TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_drop_table_if\t= FALSE;\n\n\t/**\n\t * UNSIGNED support\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_unsigned\t\t= array(\n\t\t'SMALLINT'\t=> 'INTEGER',\n\t\t'INTEGER'\t=> 'INT64',\n\t\t'FLOAT'\t\t=> 'DOUBLE PRECISION'\n\t);\n\n\t/**\n\t * NULL value representation in CREATE/ALTER TABLE statements\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_null\t\t= 'NULL';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Create database\n\t *\n\t * @param\tstring\t$db_name\n\t * @return\tbool\n\t */\n\tpublic function create_database($db_name)\n\t{\n\t\t// Firebird databases are flat files, so a path is required\n\n\t\t// Hostname is needed for remote access\n\t\tempty($this->db->hostname) OR $db_name = $this->hostname.':'.$db_name;\n\n\t\treturn parent::create_database('\"'.$db_name.'\"');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Drop database\n\t *\n\t * @param\tstring\t$db_name\t(ignored)\n\t * @return\tbool\n\t */\n\tpublic function drop_database($db_name)\n\t{\n\t\tif ( ! ibase_drop_db($this->conn_id))\n\t\t{\n\t\t\treturn ($this->db->db_debug) ? $this->db->display_error('db_unable_to_drop') : FALSE;\n\t\t}\n\t\telseif ( ! empty($this->db->data_cache['db_names']))\n\t\t{\n\t\t\t$key = array_search(strtolower($this->db->database), array_map('strtolower', $this->db->data_cache['db_names']), TRUE);\n\t\t\tif ($key !== FALSE)\n\t\t\t{\n\t\t\t\tunset($this->db->data_cache['db_names'][$key]);\n\t\t\t}\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ALTER TABLE\n\t *\n\t * @param\tstring\t$alter_type\tALTER type\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tmixed\t$field\t\tColumn definition\n\t * @return\tstring|string[]\n\t */\n\tprotected function _alter_table($alter_type, $table, $field)\n\t{\n\t\tif (in_array($alter_type, array('DROP', 'ADD'), TRUE))\n\t\t{\n\t\t\treturn parent::_alter_table($alter_type, $table, $field);\n\t\t}\n\n\t\t$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table);\n\t\t$sqls = array();\n\t\tfor ($i = 0, $c = count($field); $i < $c; $i++)\n\t\t{\n\t\t\tif ($field[$i]['_literal'] !== FALSE)\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\tif (isset($field[$i]['type']))\n\t\t\t{\n\t\t\t\t$sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identififers($field[$i]['name'])\n\t\t\t\t\t.' TYPE '.$field[$i]['type'].$field[$i]['length'];\n\t\t\t}\n\n\t\t\tif ( ! empty($field[$i]['default']))\n\t\t\t{\n\t\t\t\t$sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identifiers($field[$i]['name'])\n\t\t\t\t\t.' SET DEFAULT '.$field[$i]['default'];\n\t\t\t}\n\n\t\t\tif (isset($field[$i]['null']))\n\t\t\t{\n\t\t\t\t$sqls[] = 'UPDATE \"RDB$RELATION_FIELDS\" SET \"RDB$NULL_FLAG\" = '\n\t\t\t\t\t.($field[$i]['null'] === TRUE ? 'NULL' : '1')\n\t\t\t\t\t.' WHERE \"RDB$FIELD_NAME\" = '.$this->db->escape($field[$i]['name'])\n\t\t\t\t\t.' AND \"RDB$RELATION_NAME\" = '.$this->db->escape($table);\n\t\t\t}\n\n\t\t\tif ( ! empty($field[$i]['new_name']))\n\t\t\t{\n\t\t\t\t$sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identifiers($field[$i]['name'])\n\t\t\t\t\t.' TO '.$this->db->escape_identifiers($field[$i]['new_name']);\n\t\t\t}\n\t\t}\n\n\t\treturn $sqls;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Process column\n\t *\n\t * @param\tarray\t$field\n\t * @return\tstring\n\t */\n\tprotected function _process_column($field)\n\t{\n\t\treturn $this->db->escape_identifiers($field['name'])\n\t\t\t.' '.$field['type'].$field['length']\n\t\t\t.$field['null']\n\t\t\t.$field['unique']\n\t\t\t.$field['default'];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute TYPE\n\t *\n\t * Performs a data type mapping between different databases.\n\t *\n\t * @param\tarray\t&$attributes\n\t * @return\tvoid\n\t */\n\tprotected function _attr_type(&$attributes)\n\t{\n\t\tswitch (strtoupper($attributes['TYPE']))\n\t\t{\n\t\t\tcase 'TINYINT':\n\t\t\t\t$attributes['TYPE'] = 'SMALLINT';\n\t\t\t\t$attributes['UNSIGNED'] = FALSE;\n\t\t\t\treturn;\n\t\t\tcase 'MEDIUMINT':\n\t\t\t\t$attributes['TYPE'] = 'INTEGER';\n\t\t\t\t$attributes['UNSIGNED'] = FALSE;\n\t\t\t\treturn;\n\t\t\tcase 'INT':\n\t\t\t\t$attributes['TYPE'] = 'INTEGER';\n\t\t\t\treturn;\n\t\t\tcase 'BIGINT':\n\t\t\t\t$attributes['TYPE'] = 'INT64';\n\t\t\t\treturn;\n\t\t\tdefault: return;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute AUTO_INCREMENT\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_auto_increment(&$attributes, &$field)\n\t{\n\t\t// Not supported\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/ibase/ibase_result.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Interbase/Firebird Result Class\n *\n * This class extends the parent result class: CI_DB_result\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_ibase_result extends CI_DB_result {\n\n\t/**\n\t * Number of fields in the result set\n\t *\n\t * @return\tint\n\t */\n\tpublic function num_fields()\n\t{\n\t\treturn ibase_num_fields($this->result_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch Field Names\n\t *\n\t * Generates an array of column names\n\t *\n\t * @return\tarray\n\t */\n\tpublic function list_fields()\n\t{\n\t\t$field_names = array();\n\t\tfor ($i = 0, $num_fields = $this->num_fields(); $i < $num_fields; $i++)\n\t\t{\n\t\t\t$info = ibase_field_info($this->result_id, $i);\n\t\t\t$field_names[] = $info['name'];\n\t\t}\n\n\t\treturn $field_names;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field data\n\t *\n\t * Generates an array of objects containing field meta-data\n\t *\n\t * @return\tarray\n\t */\n\tpublic function field_data()\n\t{\n\t\t$retval = array();\n\t\tfor ($i = 0, $c = $this->num_fields(); $i < $c; $i++)\n\t\t{\n\t\t\t$info = ibase_field_info($this->result_id, $i);\n\n\t\t\t$retval[$i]\t\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t\t= $info['name'];\n\t\t\t$retval[$i]->type\t\t= $info['type'];\n\t\t\t$retval[$i]->max_length\t\t= $info['length'];\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Free the result\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function free_result()\n\t{\n\t\tibase_free_result($this->result_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - associative array\n\t *\n\t * Returns the result set as an array\n\t *\n\t * @return\tarray\n\t */\n\tprotected function _fetch_assoc()\n\t{\n\t\treturn ibase_fetch_assoc($this->result_id, IBASE_FETCH_BLOBS);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - object\n\t *\n\t * Returns the result set as an object\n\t *\n\t * @param\tstring\t$class_name\n\t * @return\tobject\n\t */\n\tprotected function _fetch_object($class_name = 'stdClass')\n\t{\n\t\t$row = ibase_fetch_object($this->result_id, IBASE_FETCH_BLOBS);\n\n\t\tif ($class_name === 'stdClass' OR ! $row)\n\t\t{\n\t\t\treturn $row;\n\t\t}\n\n\t\t$class_name = new $class_name();\n\t\tforeach ($row as $key => $value)\n\t\t{\n\t\t\t$class_name->$key = $value;\n\t\t}\n\n\t\treturn $class_name;\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/ibase/ibase_utility.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Interbase/Firebird Utility Class\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_ibase_utility extends CI_DB_utility {\n\n\t/**\n\t * Export\n\t *\n\t * @param\tstring\t$filename\n\t * @return\tmixed\n\t */\n\tprotected function _backup($filename)\n\t{\n\t\tif ($service = ibase_service_attach($this->db->hostname, $this->db->username, $this->db->password))\n\t\t{\n\t\t\t$res = ibase_backup($service, $this->db->database, $filename.'.fbk');\n\n\t\t\t// Close the service connection\n\t\t\tibase_service_detach($service);\n\t\t\treturn $res;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/ibase/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/database/drivers/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/database/drivers/mssql/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/database/drivers/mssql/mssql_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.3.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * MS SQL Database Adapter Class\n *\n * Note: _DB is an extender class that the app controller\n * creates dynamically based on whether the query builder\n * class is being used or not.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_mssql_driver extends CI_DB {\n\n\t/**\n\t * Database driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $dbdriver = 'mssql';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ORDER BY random keyword\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_random_keyword = array('NEWID()', 'RAND(%d)');\n\n\t/**\n\t * Quoted identifier flag\n\t *\n\t * Whether to use SQL-92 standard quoted identifier\n\t * (double quotes) or brackets for identifier escaping.\n\t *\n\t * @var\tbool\n\t */\n\tprotected $_quoted_identifier = TRUE;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * Appends the port number to the hostname, if needed.\n\t *\n\t * @param\tarray\t$params\n\t * @return\tvoid\n\t */\n\tpublic function __construct($params)\n\t{\n\t\tparent::__construct($params);\n\n\t\tif ( ! empty($this->port))\n\t\t{\n\t\t\t$this->hostname .= (DIRECTORY_SEPARATOR === '\\\\' ? ',' : ':').$this->port;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Non-persistent database connection\n\t *\n\t * @param\tbool\t$persistent\n\t * @return\tresource\n\t */\n\tpublic function db_connect($persistent = FALSE)\n\t{\n\t\tini_set('mssql.charset', $this->char_set);\n\t\t$this->conn_id = ($persistent)\n\t\t\t\t? mssql_pconnect($this->hostname, $this->username, $this->password)\n\t\t\t\t: mssql_connect($this->hostname, $this->username, $this->password);\n\n\t\tif ( ! $this->conn_id)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// ----------------------------------------------------------------\n\n\t\t// Select the DB... assuming a database name is specified in the config file\n\t\tif ($this->database !== '' && ! $this->db_select())\n\t\t{\n\t\t\tlog_message('error', 'Unable to select database: '.$this->database);\n\n\t\t\treturn ($this->db_debug === TRUE)\n\t\t\t\t? $this->display_error('db_unable_to_select', $this->database)\n\t\t\t\t: FALSE;\n\t\t}\n\n\t\t// Determine how identifiers are escaped\n\t\t$query = $this->query('SELECT CASE WHEN (@@OPTIONS | 256) = @@OPTIONS THEN 1 ELSE 0 END AS qi');\n\t\t$query = $query->row_array();\n\t\t$this->_quoted_identifier = empty($query) ? FALSE : (bool) $query['qi'];\n\t\t$this->_escape_char = ($this->_quoted_identifier) ? '\"' : array('[', ']');\n\n\t\treturn $this->conn_id;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Select the database\n\t *\n\t * @param\tstring\t$database\n\t * @return\tbool\n\t */\n\tpublic function db_select($database = '')\n\t{\n\t\tif ($database === '')\n\t\t{\n\t\t\t$database = $this->database;\n\t\t}\n\n\t\t// Note: Escaping is required in the event that the DB name\n\t\t// contains reserved characters.\n\t\tif (mssql_select_db('['.$database.']', $this->conn_id))\n\t\t{\n\t\t\t$this->database = $database;\n\t\t\t$this->data_cache = array();\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Execute the query\n\t *\n\t * @param\tstring\t$sql\tan SQL query\n\t * @return\tmixed\tresource if rows are returned, bool otherwise\n\t */\n\tprotected function _execute($sql)\n\t{\n\t\treturn mssql_query($sql, $this->conn_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Begin Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_begin()\n\t{\n\t\treturn $this->simple_query('BEGIN TRAN');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Commit Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_commit()\n\t{\n\t\treturn $this->simple_query('COMMIT TRAN');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Rollback Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_rollback()\n\t{\n\t\treturn $this->simple_query('ROLLBACK TRAN');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Affected Rows\n\t *\n\t * @return\tint\n\t */\n\tpublic function affected_rows()\n\t{\n\t\treturn mssql_rows_affected($this->conn_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert ID\n\t *\n\t * Returns the last id created in the Identity column.\n\t *\n\t * @return\tstring\n\t */\n\tpublic function insert_id()\n\t{\n\t\t$query = version_compare($this->version(), '8', '>=')\n\t\t\t? 'SELECT SCOPE_IDENTITY() AS last_id'\n\t\t\t: 'SELECT @@IDENTITY AS last_id';\n\n\t\t$query = $this->query($query);\n\t\t$query = $query->row();\n\t\treturn $query->last_id;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Version number query string\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _version()\n\t{\n\t\treturn \"SELECT SERVERPROPERTY('ProductVersion') AS ver\";\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * List table query\n\t *\n\t * Generates a platform-specific query string so that the table names can be fetched\n\t *\n\t * @param\tbool\t$prefix_limit\n\t * @return\tstring\n\t */\n\tprotected function _list_tables($prefix_limit = FALSE)\n\t{\n\t\t$sql = 'SELECT '.$this->escape_identifiers('name')\n\t\t\t.' FROM '.$this->escape_identifiers('sysobjects')\n\t\t\t.' WHERE '.$this->escape_identifiers('type').\" = 'U'\";\n\n\t\tif ($prefix_limit !== FALSE && $this->dbprefix !== '')\n\t\t{\n\t\t\t$sql .= ' AND '.$this->escape_identifiers('name').\" LIKE '\".$this->escape_like_str($this->dbprefix).\"%' \"\n\t\t\t\t.sprintf($this->_like_escape_str, $this->_like_escape_chr);\n\t\t}\n\n\t\treturn $sql.' ORDER BY '.$this->escape_identifiers('name');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * List column query\n\t *\n\t * Generates a platform-specific query string so that the column names can be fetched\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _list_columns($table = '')\n\t{\n\t\treturn 'SELECT COLUMN_NAME\n\t\t\tFROM INFORMATION_SCHEMA.Columns\n\t\t\tWHERE UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns an object with field data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tarray\n\t */\n\tpublic function field_data($table)\n\t{\n\t\t$sql = 'SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, COLUMN_DEFAULT\n\t\t\tFROM INFORMATION_SCHEMA.Columns\n\t\t\tWHERE UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));\n\n\t\tif (($query = $this->query($sql)) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\t\t$query = $query->result_object();\n\n\t\t$retval = array();\n\t\tfor ($i = 0, $c = count($query); $i < $c; $i++)\n\t\t{\n\t\t\t$retval[$i]\t\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t\t= $query[$i]->COLUMN_NAME;\n\t\t\t$retval[$i]->type\t\t= $query[$i]->DATA_TYPE;\n\t\t\t$retval[$i]->max_length\t\t= ($query[$i]->CHARACTER_MAXIMUM_LENGTH > 0) ? $query[$i]->CHARACTER_MAXIMUM_LENGTH : $query[$i]->NUMERIC_PRECISION;\n\t\t\t$retval[$i]->default\t\t= $query[$i]->COLUMN_DEFAULT;\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Error\n\t *\n\t * Returns an array containing code and message of the last\n\t * database error that has occurred.\n\t *\n\t * @return\tarray\n\t */\n\tpublic function error()\n\t{\n\t\t// We need this because the error info is discarded by the\n\t\t// server the first time you request it, and query() already\n\t\t// calls error() once for logging purposes when a query fails.\n\t\tstatic $error = array('code' => 0, 'message' => NULL);\n\n\t\t$message = mssql_get_last_message();\n\t\tif ( ! empty($message))\n\t\t{\n\t\t\t$error['code']    = $this->query('SELECT @@ERROR AS code')->row()->code;\n\t\t\t$error['message'] = $message;\n\t\t}\n\n\t\treturn $error;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Update statement\n\t *\n\t * Generates a platform-specific update string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @param\tarray\t$values\n\t * @return\tstring\n\t */\n\tprotected function _update($table, $values)\n\t{\n\t\t$this->qb_limit = FALSE;\n\t\t$this->qb_orderby = array();\n\t\treturn parent::_update($table, $values);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Truncate statement\n\t *\n\t * Generates a platform-specific truncate string from the supplied data\n\t *\n\t * If the database does not support the TRUNCATE statement,\n\t * then this method maps to 'DELETE FROM table'\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _truncate($table)\n\t{\n\t\treturn 'TRUNCATE TABLE '.$table;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Delete statement\n\t *\n\t * Generates a platform-specific delete string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _delete($table)\n\t{\n\t\tif ($this->qb_limit)\n\t\t{\n\t\t\treturn 'WITH ci_delete AS (SELECT TOP '.$this->qb_limit.' * FROM '.$table.$this->_compile_wh('qb_where').') DELETE FROM ci_delete';\n\t\t}\n\n\t\treturn parent::_delete($table);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * LIMIT\n\t *\n\t * Generates a platform-specific LIMIT clause\n\t *\n\t * @param\tstring\t$sql\tSQL Query\n\t * @return\tstring\n\t */\n\tprotected function _limit($sql)\n\t{\n\t\t$limit = $this->qb_offset + $this->qb_limit;\n\n\t\t// As of SQL Server 2005 (9.0.*) ROW_NUMBER() is supported,\n\t\t// however an ORDER BY clause is required for it to work\n\t\tif (version_compare($this->version(), '9', '>=') && $this->qb_offset && ! empty($this->qb_orderby))\n\t\t{\n\t\t\t$orderby = $this->_compile_order_by();\n\n\t\t\t// We have to strip the ORDER BY clause\n\t\t\t$sql = trim(substr($sql, 0, strrpos($sql, $orderby)));\n\n\t\t\t// Get the fields to select from our subquery, so that we can avoid CI_rownum appearing in the actual results\n\t\t\tif (count($this->qb_select) === 0 OR strpos(implode(',', $this->qb_select), '*') !== FALSE)\n\t\t\t{\n\t\t\t\t$select = '*'; // Inevitable\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Use only field names and their aliases, everything else is out of our scope.\n\t\t\t\t$select = array();\n\t\t\t\t$field_regexp = ($this->_quoted_identifier)\n\t\t\t\t\t? '(\"[^\\\"]+\")' : '(\\[[^\\]]+\\])';\n\t\t\t\tfor ($i = 0, $c = count($this->qb_select); $i < $c; $i++)\n\t\t\t\t{\n\t\t\t\t\t$select[] = preg_match('/(?:\\s|\\.)'.$field_regexp.'$/i', $this->qb_select[$i], $m)\n\t\t\t\t\t\t? $m[1] : $this->qb_select[$i];\n\t\t\t\t}\n\t\t\t\t$select = implode(', ', $select);\n\t\t\t}\n\n\t\t\treturn 'SELECT '.$select.\" FROM (\\n\\n\"\n\t\t\t\t.preg_replace('/^(SELECT( DISTINCT)?)/i', '\\\\1 ROW_NUMBER() OVER('.trim($orderby).') AS '.$this->escape_identifiers('CI_rownum').', ', $sql)\n\t\t\t\t.\"\\n\\n) \".$this->escape_identifiers('CI_subquery')\n\t\t\t\t.\"\\nWHERE \".$this->escape_identifiers('CI_rownum').' BETWEEN '.($this->qb_offset + 1).' AND '.$limit;\n\t\t}\n\n\t\treturn preg_replace('/(^\\SELECT (DISTINCT)?)/i','\\\\1 TOP '.$limit.' ', $sql);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert batch statement\n\t *\n\t * Generates a platform-specific insert string from the supplied data.\n\t *\n\t * @param\tstring\t$table\tTable name\n\t * @param\tarray\t$keys\tINSERT keys\n\t * @param\tarray\t$values\tINSERT values\n\t * @return\tstring|bool\n\t */\n\tprotected function _insert_batch($table, $keys, $values)\n\t{\n\t\t// Multiple-value inserts are only supported as of SQL Server 2008\n\t\tif (version_compare($this->version(), '10', '>='))\n\t\t{\n\t\t\treturn parent::_insert_batch($table, $keys, $values);\n\t\t}\n\n\t\treturn ($this->db_debug) ? $this->display_error('db_unsupported_feature') : FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Close DB Connection\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _close()\n\t{\n\t\tmssql_close($this->conn_id);\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/mssql/mssql_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.3.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * MS SQL Forge Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_mssql_forge extends CI_DB_forge {\n\n\t/**\n\t * CREATE TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_table_if\t= \"IF NOT EXISTS (SELECT * FROM sysobjects WHERE ID = object_id(N'%s') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)\\nCREATE TABLE\";\n\n\t/**\n\t * DROP TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_drop_table_if\t= \"IF EXISTS (SELECT * FROM sysobjects WHERE ID = object_id(N'%s') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)\\nDROP TABLE\";\n\n\t/**\n\t * UNSIGNED support\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_unsigned\t\t= array(\n\t\t'TINYINT'\t=> 'SMALLINT',\n\t\t'SMALLINT'\t=> 'INT',\n\t\t'INT'\t\t=> 'BIGINT',\n\t\t'REAL'\t\t=> 'FLOAT'\n\t);\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ALTER TABLE\n\t *\n\t * @param\tstring\t$alter_type\tALTER type\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tmixed\t$field\t\tColumn definition\n\t * @return\tstring|string[]\n\t */\n\tprotected function _alter_table($alter_type, $table, $field)\n\t{\n\t\tif (in_array($alter_type, array('ADD', 'DROP'), TRUE))\n\t\t{\n\t\t\treturn parent::_alter_table($alter_type, $table, $field);\n\t\t}\n\n\t\t$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' ALTER COLUMN ';\n\t\t$sqls = array();\n\t\tfor ($i = 0, $c = count($field); $i < $c; $i++)\n\t\t{\n\t\t\t$sqls[] = $sql.$this->_process_column($field[$i]);\n\t\t}\n\n\t\treturn $sqls;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute TYPE\n\t *\n\t * Performs a data type mapping between different databases.\n\t *\n\t * @param\tarray\t&$attributes\n\t * @return\tvoid\n\t */\n\tprotected function _attr_type(&$attributes)\n\t{\n\t\tif (isset($attributes['CONSTRAINT']) && strpos($attributes['TYPE'], 'INT') !== FALSE)\n\t\t{\n\t\t\tunset($attributes['CONSTRAINT']);\n\t\t}\n\n\t\tswitch (strtoupper($attributes['TYPE']))\n\t\t{\n\t\t\tcase 'MEDIUMINT':\n\t\t\t\t$attributes['TYPE'] = 'INTEGER';\n\t\t\t\t$attributes['UNSIGNED'] = FALSE;\n\t\t\t\treturn;\n\t\t\tcase 'INTEGER':\n\t\t\t\t$attributes['TYPE'] = 'INT';\n\t\t\t\treturn;\n\t\t\tdefault: return;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute AUTO_INCREMENT\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_auto_increment(&$attributes, &$field)\n\t{\n\t\tif ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE)\n\t\t{\n\t\t\t$field['auto_increment'] = ' IDENTITY(1,1)';\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/mssql/mssql_result.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.3.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * MSSQL Result Class\n *\n * This class extends the parent result class: CI_DB_result\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_mssql_result extends CI_DB_result {\n\n\t/**\n\t * Number of rows in the result set\n\t *\n\t * @return\tint\n\t */\n\tpublic function num_rows()\n\t{\n\t\treturn is_int($this->num_rows)\n\t\t\t? $this->num_rows\n\t\t\t: $this->num_rows = mssql_num_rows($this->result_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Number of fields in the result set\n\t *\n\t * @return\tint\n\t */\n\tpublic function num_fields()\n\t{\n\t\treturn mssql_num_fields($this->result_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch Field Names\n\t *\n\t * Generates an array of column names\n\t *\n\t * @return\tarray\n\t */\n\tpublic function list_fields()\n\t{\n\t\t$field_names = array();\n\t\tmssql_field_seek($this->result_id, 0);\n\t\twhile ($field = mssql_fetch_field($this->result_id))\n\t\t{\n\t\t\t$field_names[] = $field->name;\n\t\t}\n\n\t\treturn $field_names;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field data\n\t *\n\t * Generates an array of objects containing field meta-data\n\t *\n\t * @return\tarray\n\t */\n\tpublic function field_data()\n\t{\n\t\t$retval = array();\n\t\tfor ($i = 0, $c = $this->num_fields(); $i < $c; $i++)\n\t\t{\n\t\t\t$field = mssql_fetch_field($this->result_id, $i);\n\n\t\t\t$retval[$i]\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t= $field->name;\n\t\t\t$retval[$i]->type\t= $field->type;\n\t\t\t$retval[$i]->max_length\t= $field->max_length;\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Free the result\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function free_result()\n\t{\n\t\tif (is_resource($this->result_id))\n\t\t{\n\t\t\tmssql_free_result($this->result_id);\n\t\t\t$this->result_id = FALSE;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Data Seek\n\t *\n\t * Moves the internal pointer to the desired offset. We call\n\t * this internally before fetching results to make sure the\n\t * result set starts at zero.\n\t *\n\t * @param\tint\t$n\n\t * @return\tbool\n\t */\n\tpublic function data_seek($n = 0)\n\t{\n\t\treturn mssql_data_seek($this->result_id, $n);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - associative array\n\t *\n\t * Returns the result set as an array\n\t *\n\t * @return\tarray\n\t */\n\tprotected function _fetch_assoc()\n\t{\n\t\treturn mssql_fetch_assoc($this->result_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - object\n\t *\n\t * Returns the result set as an object\n\t *\n\t * @param\tstring\t$class_name\n\t * @return\tobject\n\t */\n\tprotected function _fetch_object($class_name = 'stdClass')\n\t{\n\t\t$row = mssql_fetch_object($this->result_id);\n\n\t\tif ($class_name === 'stdClass' OR ! $row)\n\t\t{\n\t\t\treturn $row;\n\t\t}\n\n\t\t$class_name = new $class_name();\n\t\tforeach ($row as $key => $value)\n\t\t{\n\t\t\t$class_name->$key = $value;\n\t\t}\n\n\t\treturn $class_name;\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/mssql/mssql_utility.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.3.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * MS SQL Utility Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_mssql_utility extends CI_DB_utility {\n\n\t/**\n\t * List databases statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_list_databases\t= 'EXEC sp_helpdb'; // Can also be: EXEC sp_databases\n\n\t/**\n\t * OPTIMIZE TABLE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_optimize_table\t= 'ALTER INDEX all ON %s REORGANIZE';\n\n\t/**\n\t * Export\n\t *\n\t * @param\tarray\t$params\tPreferences\n\t * @return\tbool\n\t */\n\tprotected function _backup($params = array())\n\t{\n\t\t// Currently unsupported\n\t\treturn $this->db->display_error('db_unsupported_feature');\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/mysql/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/database/drivers/mysql/mysql_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * MySQL Database Adapter Class\n *\n * Note: _DB is an extender class that the app controller\n * creates dynamically based on whether the query builder\n * class is being used or not.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_mysql_driver extends CI_DB {\n\n\t/**\n\t * Database driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $dbdriver = 'mysql';\n\n\t/**\n\t * Compression flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $compress = FALSE;\n\n\t/**\n\t * DELETE hack flag\n\t *\n\t * Whether to use the MySQL \"delete hack\" which allows the number\n\t * of affected rows to be shown. Uses a preg_replace when enabled,\n\t * adding a bit more processing to all queries.\n\t *\n\t * @var\tbool\n\t */\n\tpublic $delete_hack = TRUE;\n\n\t/**\n\t * Strict ON flag\n\t *\n\t * Whether we're running in strict SQL mode.\n\t *\n\t * @var\tbool\n\t */\n\tpublic $stricton;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Identifier escape character\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_escape_char = '`';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * @param\tarray\t$params\n\t * @return\tvoid\n\t */\n\tpublic function __construct($params)\n\t{\n\t\tparent::__construct($params);\n\n\t\tif ( ! empty($this->port))\n\t\t{\n\t\t\t$this->hostname .= ':'.$this->port;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Non-persistent database connection\n\t *\n\t * @param\tbool\t$persistent\n\t * @return\tresource\n\t */\n\tpublic function db_connect($persistent = FALSE)\n\t{\n\t\t$client_flags = ($this->compress === FALSE) ? 0 : MYSQL_CLIENT_COMPRESS;\n\n\t\tif ($this->encrypt === TRUE)\n\t\t{\n\t\t\t$client_flags = $client_flags | MYSQL_CLIENT_SSL;\n\t\t}\n\n\t\t// Error suppression is necessary mostly due to PHP 5.5+ issuing E_DEPRECATED messages\n\t\t$this->conn_id = ($persistent === TRUE)\n\t\t\t? mysql_pconnect($this->hostname, $this->username, $this->password, $client_flags)\n\t\t\t: mysql_connect($this->hostname, $this->username, $this->password, TRUE, $client_flags);\n\n\t\t// ----------------------------------------------------------------\n\n\t\t// Select the DB... assuming a database name is specified in the config file\n\t\tif ($this->database !== '' && ! $this->db_select())\n\t\t{\n\t\t\tlog_message('error', 'Unable to select database: '.$this->database);\n\n\t\t\treturn ($this->db_debug === TRUE)\n\t\t\t\t? $this->display_error('db_unable_to_select', $this->database)\n\t\t\t\t: FALSE;\n\t\t}\n\n\t\tif (is_resource($this->conn_id))\n\t\t{\n\t\t\tif ( ! mysql_set_charset($this->char_set, $this->conn_id))\n\t\t\t{\n\t\t\t\tlog_message('error', \"Database: Unable to set the configured connection charset ('{$this->char_set}').\");\n\t\t\t\t$this->close();\n\t\t\t\treturn ($this->db->debug) ? $this->display_error('db_unable_to_set_charset', $this->char_set) : FALSE;\n\t\t\t}\n\n\t\t\tif (isset($this->stricton))\n\t\t\t{\n\t\t\t\tif ($this->stricton)\n\t\t\t\t{\n\t\t\t\t\t$this->simple_query('SET SESSION sql_mode = CONCAT(@@sql_mode, \",\", \"STRICT_ALL_TABLES\")');\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$this->simple_query(\n\t\t\t\t\t\t'SET SESSION sql_mode =\n\t\t\t\t\t\tREPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(\n\t\t\t\t\t\t@@sql_mode,\n\t\t\t\t\t\t\"STRICT_ALL_TABLES,\", \"\"),\n\t\t\t\t\t\t\",STRICT_ALL_TABLES\", \"\"),\n\t\t\t\t\t\t\"STRICT_ALL_TABLES\", \"\"),\n\t\t\t\t\t\t\"STRICT_TRANS_TABLES,\", \"\"),\n\t\t\t\t\t\t\",STRICT_TRANS_TABLES\", \"\"),\n\t\t\t\t\t\t\"STRICT_TRANS_TABLES\", \"\")'\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn $this->conn_id;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Reconnect\n\t *\n\t * Keep / reestablish the db connection if no queries have been\n\t * sent for a length of time exceeding the server's idle timeout\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function reconnect()\n\t{\n\t\tif (mysql_ping($this->conn_id) === FALSE)\n\t\t{\n\t\t\t$this->conn_id = FALSE;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Select the database\n\t *\n\t * @param\tstring\t$database\n\t * @return\tbool\n\t */\n\tpublic function db_select($database = '')\n\t{\n\t\tif ($database === '')\n\t\t{\n\t\t\t$database = $this->database;\n\t\t}\n\n\t\tif (mysql_select_db($database, $this->conn_id))\n\t\t{\n\t\t\t$this->database = $database;\n\t\t\t$this->data_cache = array();\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Database version number\n\t *\n\t * @return\tstring\n\t */\n\tpublic function version()\n\t{\n\t\tif (isset($this->data_cache['version']))\n\t\t{\n\t\t\treturn $this->data_cache['version'];\n\t\t}\n\n\t\tif ( ! $this->conn_id OR ($version = mysql_get_server_info($this->conn_id)) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn $this->data_cache['version'] = $version;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Execute the query\n\t *\n\t * @param\tstring\t$sql\tan SQL query\n\t * @return\tmixed\n\t */\n\tprotected function _execute($sql)\n\t{\n\t\treturn mysql_query($this->_prep_query($sql), $this->conn_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Prep the query\n\t *\n\t * If needed, each database adapter can prep the query string\n\t *\n\t * @param\tstring\t$sql\tan SQL query\n\t * @return\tstring\n\t */\n\tprotected function _prep_query($sql)\n\t{\n\t\t// mysql_affected_rows() returns 0 for \"DELETE FROM TABLE\" queries. This hack\n\t\t// modifies the query so that it a proper number of affected rows is returned.\n\t\tif ($this->delete_hack === TRUE && preg_match('/^\\s*DELETE\\s+FROM\\s+(\\S+)\\s*$/i', $sql))\n\t\t{\n\t\t\treturn trim($sql).' WHERE 1=1';\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Begin Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_begin()\n\t{\n\t\t$this->simple_query('SET AUTOCOMMIT=0');\n\t\treturn $this->simple_query('START TRANSACTION'); // can also be BEGIN or BEGIN WORK\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Commit Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_commit()\n\t{\n\t\tif ($this->simple_query('COMMIT'))\n\t\t{\n\t\t\t$this->simple_query('SET AUTOCOMMIT=1');\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Rollback Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_rollback()\n\t{\n\t\tif ($this->simple_query('ROLLBACK'))\n\t\t{\n\t\t\t$this->simple_query('SET AUTOCOMMIT=1');\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Platform-dependent string escape\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tprotected function _escape_str($str)\n\t{\n\t\treturn mysql_real_escape_string($str, $this->conn_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Affected Rows\n\t *\n\t * @return\tint\n\t */\n\tpublic function affected_rows()\n\t{\n\t\treturn mysql_affected_rows($this->conn_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert ID\n\t *\n\t * @return\tint\n\t */\n\tpublic function insert_id()\n\t{\n\t\treturn mysql_insert_id($this->conn_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * List table query\n\t *\n\t * Generates a platform-specific query string so that the table names can be fetched\n\t *\n\t * @param\tbool\t$prefix_limit\n\t * @return\tstring\n\t */\n\tprotected function _list_tables($prefix_limit = FALSE)\n\t{\n\t\t$sql = 'SHOW TABLES FROM '.$this->_escape_char.$this->database.$this->_escape_char;\n\n\t\tif ($prefix_limit !== FALSE && $this->dbprefix !== '')\n\t\t{\n\t\t\treturn $sql.\" LIKE '\".$this->escape_like_str($this->dbprefix).\"%'\";\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show column query\n\t *\n\t * Generates a platform-specific query string so that the column names can be fetched\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _list_columns($table = '')\n\t{\n\t\treturn 'SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns an object with field data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tarray\n\t */\n\tpublic function field_data($table)\n\t{\n\t\tif (($query = $this->query('SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE))) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\t\t$query = $query->result_object();\n\n\t\t$retval = array();\n\t\tfor ($i = 0, $c = count($query); $i < $c; $i++)\n\t\t{\n\t\t\t$retval[$i]\t\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t\t= $query[$i]->Field;\n\n\t\t\tsscanf($query[$i]->Type, '%[a-z](%d)',\n\t\t\t\t$retval[$i]->type,\n\t\t\t\t$retval[$i]->max_length\n\t\t\t);\n\n\t\t\t$retval[$i]->default\t\t= $query[$i]->Default;\n\t\t\t$retval[$i]->primary_key\t= (int) ($query[$i]->Key === 'PRI');\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Error\n\t *\n\t * Returns an array containing code and message of the last\n\t * database error that has occurred.\n\t *\n\t * @return\tarray\n\t */\n\tpublic function error()\n\t{\n\t\treturn array('code' => mysql_errno($this->conn_id), 'message' => mysql_error($this->conn_id));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * FROM tables\n\t *\n\t * Groups tables in FROM clauses if needed, so there is no confusion\n\t * about operator precedence.\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _from_tables()\n\t{\n\t\tif ( ! empty($this->qb_join) && count($this->qb_from) > 1)\n\t\t{\n\t\t\treturn '('.implode(', ', $this->qb_from).')';\n\t\t}\n\n\t\treturn implode(', ', $this->qb_from);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Close DB Connection\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _close()\n\t{\n\t\t// Error suppression to avoid annoying E_WARNINGs in cases\n\t\t// where the connection has already been closed for some reason.\n\t\t@mysql_close($this->conn_id);\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/mysql/mysql_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * MySQL Forge Class\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_mysql_forge extends CI_DB_forge {\n\n\t/**\n\t * CREATE DATABASE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_database\t= 'CREATE DATABASE %s CHARACTER SET %s COLLATE %s';\n\n\t/**\n\t * CREATE TABLE keys flag\n\t *\n\t * Whether table keys are created from within the\n\t * CREATE TABLE statement.\n\t *\n\t * @var\tbool\n\t */\n\tprotected $_create_table_keys\t= TRUE;\n\n\t/**\n\t * UNSIGNED support\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_unsigned\t\t= array(\n\t\t'TINYINT',\n\t\t'SMALLINT',\n\t\t'MEDIUMINT',\n\t\t'INT',\n\t\t'INTEGER',\n\t\t'BIGINT',\n\t\t'REAL',\n\t\t'DOUBLE',\n\t\t'DOUBLE PRECISION',\n\t\t'FLOAT',\n\t\t'DECIMAL',\n\t\t'NUMERIC'\n\t);\n\n\t/**\n\t * NULL value representation in CREATE/ALTER TABLE statements\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_null = 'NULL';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * CREATE TABLE attributes\n\t *\n\t * @param\tarray\t$attributes\tAssociative array of table attributes\n\t * @return\tstring\n\t */\n\tprotected function _create_table_attr($attributes)\n\t{\n\t\t$sql = '';\n\n\t\tforeach (array_keys($attributes) as $key)\n\t\t{\n\t\t\tif (is_string($key))\n\t\t\t{\n\t\t\t\t$sql .= ' '.strtoupper($key).' = '.$attributes[$key];\n\t\t\t}\n\t\t}\n\n\t\tif ( ! empty($this->db->char_set) && ! strpos($sql, 'CHARACTER SET') && ! strpos($sql, 'CHARSET'))\n\t\t{\n\t\t\t$sql .= ' DEFAULT CHARACTER SET = '.$this->db->char_set;\n\t\t}\n\n\t\tif ( ! empty($this->db->dbcollat) && ! strpos($sql, 'COLLATE'))\n\t\t{\n\t\t\t$sql .= ' COLLATE = '.$this->db->dbcollat;\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ALTER TABLE\n\t *\n\t * @param\tstring\t$alter_type\tALTER type\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tmixed\t$field\t\tColumn definition\n\t * @return\tstring|string[]\n\t */\n\tprotected function _alter_table($alter_type, $table, $field)\n\t{\n\t\tif ($alter_type === 'DROP')\n\t\t{\n\t\t\treturn parent::_alter_table($alter_type, $table, $field);\n\t\t}\n\n\t\t$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table);\n\t\tfor ($i = 0, $c = count($field); $i < $c; $i++)\n\t\t{\n\t\t\tif ($field[$i]['_literal'] !== FALSE)\n\t\t\t{\n\t\t\t\t$field[$i] = ($alter_type === 'ADD')\n\t\t\t\t\t\t? \"\\n\\tADD \".$field[$i]['_literal']\n\t\t\t\t\t\t: \"\\n\\tMODIFY \".$field[$i]['_literal'];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif ($alter_type === 'ADD')\n\t\t\t\t{\n\t\t\t\t\t$field[$i]['_literal'] = \"\\n\\tADD \";\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$field[$i]['_literal'] = empty($field[$i]['new_name']) ? \"\\n\\tMODIFY \" : \"\\n\\tCHANGE \";\n\t\t\t\t}\n\n\t\t\t\t$field[$i] = $field[$i]['_literal'].$this->_process_column($field[$i]);\n\t\t\t}\n\t\t}\n\n\t\treturn array($sql.implode(',', $field));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Process column\n\t *\n\t * @param\tarray\t$field\n\t * @return\tstring\n\t */\n\tprotected function _process_column($field)\n\t{\n\t\t$extra_clause = isset($field['after'])\n\t\t\t? ' AFTER '.$this->db->escape_identifiers($field['after']) : '';\n\n\t\tif (empty($extra_clause) && isset($field['first']) && $field['first'] === TRUE)\n\t\t{\n\t\t\t$extra_clause = ' FIRST';\n\t\t}\n\n\t\treturn $this->db->escape_identifiers($field['name'])\n\t\t\t.(empty($field['new_name']) ? '' : ' '.$this->db->escape_identifiers($field['new_name']))\n\t\t\t.' '.$field['type'].$field['length']\n\t\t\t.$field['unsigned']\n\t\t\t.$field['null']\n\t\t\t.$field['default']\n\t\t\t.$field['auto_increment']\n\t\t\t.$field['unique']\n\t\t\t.(empty($field['comment']) ? '' : ' COMMENT '.$field['comment'])\n\t\t\t.$extra_clause;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Process indexes\n\t *\n\t * @param\tstring\t$table\t(ignored)\n\t * @return\tstring\n\t */\n\tprotected function _process_indexes($table)\n\t{\n\t\t$sql = '';\n\n\t\tfor ($i = 0, $c = count($this->keys); $i < $c; $i++)\n\t\t{\n\t\t\tif (is_array($this->keys[$i]))\n\t\t\t{\n\t\t\t\tfor ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++)\n\t\t\t\t{\n\t\t\t\t\tif ( ! isset($this->fields[$this->keys[$i][$i2]]))\n\t\t\t\t\t{\n\t\t\t\t\t\tunset($this->keys[$i][$i2]);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telseif ( ! isset($this->fields[$this->keys[$i]]))\n\t\t\t{\n\t\t\t\tunset($this->keys[$i]);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tis_array($this->keys[$i]) OR $this->keys[$i] = array($this->keys[$i]);\n\n\t\t\t$sql .= \",\\n\\tKEY \".$this->db->escape_identifiers(implode('_', $this->keys[$i]))\n\t\t\t\t.' ('.implode(', ', $this->db->escape_identifiers($this->keys[$i])).')';\n\t\t}\n\n\t\t$this->keys = array();\n\n\t\treturn $sql;\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/mysql/mysql_result.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * MySQL Result Class\n *\n * This class extends the parent result class: CI_DB_result\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_mysql_result extends CI_DB_result {\n\n\t/**\n\t * Class constructor\n\t *\n\t * @param\tobject\t&$driver_object\n\t * @return\tvoid\n\t */\n\tpublic function __construct(&$driver_object)\n\t{\n\t\tparent::__construct($driver_object);\n\n\t\t// Required, due to mysql_data_seek() causing nightmares\n\t\t// with empty result sets\n\t\t$this->num_rows = mysql_num_rows($this->result_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Number of rows in the result set\n\t *\n\t * @return\tint\n\t */\n\tpublic function num_rows()\n\t{\n\t\treturn $this->num_rows;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Number of fields in the result set\n\t *\n\t * @return\tint\n\t */\n\tpublic function num_fields()\n\t{\n\t\treturn mysql_num_fields($this->result_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch Field Names\n\t *\n\t * Generates an array of column names\n\t *\n\t * @return\tarray\n\t */\n\tpublic function list_fields()\n\t{\n\t\t$field_names = array();\n\t\tmysql_field_seek($this->result_id, 0);\n\t\twhile ($field = mysql_fetch_field($this->result_id))\n\t\t{\n\t\t\t$field_names[] = $field->name;\n\t\t}\n\n\t\treturn $field_names;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field data\n\t *\n\t * Generates an array of objects containing field meta-data\n\t *\n\t * @return\tarray\n\t */\n\tpublic function field_data()\n\t{\n\t\t$retval = array();\n\t\tfor ($i = 0, $c = $this->num_fields(); $i < $c; $i++)\n\t\t{\n\t\t\t$retval[$i]\t\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t\t= mysql_field_name($this->result_id, $i);\n\t\t\t$retval[$i]->type\t\t= mysql_field_type($this->result_id, $i);\n\t\t\t$retval[$i]->max_length\t\t= mysql_field_len($this->result_id, $i);\n\t\t\t$retval[$i]->primary_key\t= (int) (strpos(mysql_field_flags($this->result_id, $i), 'primary_key') !== FALSE);\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Free the result\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function free_result()\n\t{\n\t\tif (is_resource($this->result_id))\n\t\t{\n\t\t\tmysql_free_result($this->result_id);\n\t\t\t$this->result_id = FALSE;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Data Seek\n\t *\n\t * Moves the internal pointer to the desired offset. We call\n\t * this internally before fetching results to make sure the\n\t * result set starts at zero.\n\t *\n\t * @param\tint\t$n\n\t * @return\tbool\n\t */\n\tpublic function data_seek($n = 0)\n\t{\n\t\treturn $this->num_rows\n\t\t\t? mysql_data_seek($this->result_id, $n)\n\t\t\t: FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - associative array\n\t *\n\t * Returns the result set as an array\n\t *\n\t * @return\tarray\n\t */\n\tprotected function _fetch_assoc()\n\t{\n\t\treturn mysql_fetch_assoc($this->result_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - object\n\t *\n\t * Returns the result set as an object\n\t *\n\t * @param\tstring\t$class_name\n\t * @return\tobject\n\t */\n\tprotected function _fetch_object($class_name = 'stdClass')\n\t{\n\t\treturn mysql_fetch_object($this->result_id, $class_name);\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/mysql/mysql_utility.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * MySQL Utility Class\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_mysql_utility extends CI_DB_utility {\n\n\t/**\n\t * List databases statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_list_databases\t= 'SHOW DATABASES';\n\n\t/**\n\t * OPTIMIZE TABLE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_optimize_table\t= 'OPTIMIZE TABLE %s';\n\n\t/**\n\t * REPAIR TABLE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_repair_table\t= 'REPAIR TABLE %s';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Export\n\t *\n\t * @param\tarray\t$params\tPreferences\n\t * @return\tmixed\n\t */\n\tprotected function _backup($params = array())\n\t{\n\t\tif (count($params) === 0)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Extract the prefs for simplicity\n\t\textract($params);\n\n\t\t// Build the output\n\t\t$output = '';\n\n\t\t// Do we need to include a statement to disable foreign key checks?\n\t\tif ($foreign_key_checks === FALSE)\n\t\t{\n\t\t\t$output .= 'SET foreign_key_checks = 0;'.$newline;\n\t\t}\n\n\t\tforeach ( (array) $tables as $table)\n\t\t{\n\t\t\t// Is the table in the \"ignore\" list?\n\t\t\tif (in_array($table, (array) $ignore, TRUE))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Get the table schema\n\t\t\t$query = $this->db->query('SHOW CREATE TABLE '.$this->db->escape_identifiers($this->db->database.'.'.$table));\n\n\t\t\t// No result means the table name was invalid\n\t\t\tif ($query === FALSE)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Write out the table schema\n\t\t\t$output .= '#'.$newline.'# TABLE STRUCTURE FOR: '.$table.$newline.'#'.$newline.$newline;\n\n\t\t\tif ($add_drop === TRUE)\n\t\t\t{\n\t\t\t\t$output .= 'DROP TABLE IF EXISTS '.$this->db->protect_identifiers($table).';'.$newline.$newline;\n\t\t\t}\n\n\t\t\t$i = 0;\n\t\t\t$result = $query->result_array();\n\t\t\tforeach ($result[0] as $val)\n\t\t\t{\n\t\t\t\tif ($i++ % 2)\n\t\t\t\t{\n\t\t\t\t\t$output .= $val.';'.$newline.$newline;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If inserts are not needed we're done...\n\t\t\tif ($add_insert === FALSE)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Grab all the data from the current table\n\t\t\t$query = $this->db->query('SELECT * FROM '.$this->db->protect_identifiers($table));\n\n\t\t\tif ($query->num_rows() === 0)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Fetch the field names and determine if the field is an\n\t\t\t// integer type. We use this info to decide whether to\n\t\t\t// surround the data with quotes or not\n\n\t\t\t$i = 0;\n\t\t\t$field_str = '';\n\t\t\t$is_int = array();\n\t\t\twhile ($field = mysql_fetch_field($query->result_id))\n\t\t\t{\n\t\t\t\t// Most versions of MySQL store timestamp as a string\n\t\t\t\t$is_int[$i] = in_array(strtolower(mysql_field_type($query->result_id, $i)),\n\t\t\t\t\t\t\tarray('tinyint', 'smallint', 'mediumint', 'int', 'bigint'), //, 'timestamp'),\n\t\t\t\t\t\t\tTRUE);\n\n\t\t\t\t// Create a string of field names\n\t\t\t\t$field_str .= $this->db->escape_identifiers($field->name).', ';\n\t\t\t\t$i++;\n\t\t\t}\n\n\t\t\t// Trim off the end comma\n\t\t\t$field_str = preg_replace('/, $/' , '', $field_str);\n\n\t\t\t// Build the insert string\n\t\t\tforeach ($query->result_array() as $row)\n\t\t\t{\n\t\t\t\t$val_str = '';\n\n\t\t\t\t$i = 0;\n\t\t\t\tforeach ($row as $v)\n\t\t\t\t{\n\t\t\t\t\t// Is the value NULL?\n\t\t\t\t\tif ($v === NULL)\n\t\t\t\t\t{\n\t\t\t\t\t\t$val_str .= 'NULL';\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// Escape the data if it's not an integer\n\t\t\t\t\t\t$val_str .= ($is_int[$i] === FALSE) ? $this->db->escape($v) : $v;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Append a comma\n\t\t\t\t\t$val_str .= ', ';\n\t\t\t\t\t$i++;\n\t\t\t\t}\n\n\t\t\t\t// Remove the comma at the end of the string\n\t\t\t\t$val_str = preg_replace('/, $/' , '', $val_str);\n\n\t\t\t\t// Build the INSERT string\n\t\t\t\t$output .= 'INSERT INTO '.$this->db->protect_identifiers($table).' ('.$field_str.') VALUES ('.$val_str.');'.$newline;\n\t\t\t}\n\n\t\t\t$output .= $newline.$newline;\n\t\t}\n\n\t\t// Do we need to include a statement to re-enable foreign key checks?\n\t\tif ($foreign_key_checks === FALSE)\n\t\t{\n\t\t\t$output .= 'SET foreign_key_checks = 1;'.$newline;\n\t\t}\n\n\t\treturn $output;\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/mysqli/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/database/drivers/mysqli/mysqli_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.3.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * MySQLi Database Adapter Class\n *\n * Note: _DB is an extender class that the app controller\n * creates dynamically based on whether the query builder\n * class is being used or not.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_mysqli_driver extends CI_DB {\n\n\t/**\n\t * Database driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $dbdriver = 'mysqli';\n\n\t/**\n\t * Compression flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $compress = FALSE;\n\n\t/**\n\t * DELETE hack flag\n\t *\n\t * Whether to use the MySQL \"delete hack\" which allows the number\n\t * of affected rows to be shown. Uses a preg_replace when enabled,\n\t * adding a bit more processing to all queries.\n\t *\n\t * @var\tbool\n\t */\n\tpublic $delete_hack = TRUE;\n\n\t/**\n\t * Strict ON flag\n\t *\n\t * Whether we're running in strict SQL mode.\n\t *\n\t * @var\tbool\n\t */\n\tpublic $stricton;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Identifier escape character\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_escape_char = '`';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * MySQLi object\n\t *\n\t * Has to be preserved without being assigned to $conn_id.\n\t *\n\t * @var\tMySQLi\n\t */\n\tprotected $_mysqli;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Database connection\n\t *\n\t * @param\tbool\t$persistent\n\t * @return\tobject\n\t */\n\tpublic function db_connect($persistent = FALSE)\n\t{\n\t\t// PHP 8.1 changes default error handling mode from silent to exceptions - reverse that\n\t\tif (is_php('8.1'))\n\t\t{\n\t\t\t$mysqli_driver = new mysqli_driver();\n\t\t\t$mysqli_driver->report_mode = MYSQLI_REPORT_OFF;\n\t\t}\n\n\t\t// Do we have a socket path?\n\t\tif ($this->hostname[0] === '/')\n\t\t{\n\t\t\t$hostname = NULL;\n\t\t\t$port = NULL;\n\t\t\t$socket = $this->hostname;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$hostname = ($persistent === TRUE)\n\t\t\t\t? 'p:'.$this->hostname : $this->hostname;\n\t\t\t$port = empty($this->port) ? NULL : $this->port;\n\t\t\t$socket = NULL;\n\t\t}\n\n\t\t$client_flags = ($this->compress === TRUE) ? MYSQLI_CLIENT_COMPRESS : 0;\n\t\t$this->_mysqli = mysqli_init();\n\n\t\t$this->_mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, 10);\n\n\t\tif (isset($this->stricton))\n\t\t{\n\t\t\tif ($this->stricton)\n\t\t\t{\n\t\t\t\t$this->_mysqli->options(MYSQLI_INIT_COMMAND, 'SET SESSION sql_mode = CONCAT(@@sql_mode, \",\", \"STRICT_ALL_TABLES\")');\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$this->_mysqli->options(MYSQLI_INIT_COMMAND,\n\t\t\t\t\t'SET SESSION sql_mode =\n\t\t\t\t\tREPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(\n\t\t\t\t\t@@sql_mode,\n\t\t\t\t\t\"STRICT_ALL_TABLES,\", \"\"),\n\t\t\t\t\t\",STRICT_ALL_TABLES\", \"\"),\n\t\t\t\t\t\"STRICT_ALL_TABLES\", \"\"),\n\t\t\t\t\t\"STRICT_TRANS_TABLES,\", \"\"),\n\t\t\t\t\t\",STRICT_TRANS_TABLES\", \"\"),\n\t\t\t\t\t\"STRICT_TRANS_TABLES\", \"\")'\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (is_array($this->encrypt))\n\t\t{\n\t\t\t$ssl = array();\n\t\t\tempty($this->encrypt['ssl_key'])    OR $ssl['key']    = $this->encrypt['ssl_key'];\n\t\t\tempty($this->encrypt['ssl_cert'])   OR $ssl['cert']   = $this->encrypt['ssl_cert'];\n\t\t\tempty($this->encrypt['ssl_ca'])     OR $ssl['ca']     = $this->encrypt['ssl_ca'];\n\t\t\tempty($this->encrypt['ssl_capath']) OR $ssl['capath'] = $this->encrypt['ssl_capath'];\n\t\t\tempty($this->encrypt['ssl_cipher']) OR $ssl['cipher'] = $this->encrypt['ssl_cipher'];\n\n\t\t\tif (isset($this->encrypt['ssl_verify']))\n\t\t\t{\n\t\t\t\t$client_flags |= MYSQLI_CLIENT_SSL;\n\n\t\t\t\tif ($this->encrypt['ssl_verify'])\n\t\t\t\t{\n\t\t\t\t\tdefined('MYSQLI_OPT_SSL_VERIFY_SERVER_CERT') && $this->_mysqli->options(MYSQLI_OPT_SSL_VERIFY_SERVER_CERT, TRUE);\n\t\t\t\t}\n\t\t\t\t// Apparently (when it exists), setting MYSQLI_OPT_SSL_VERIFY_SERVER_CERT\n\t\t\t\t// to FALSE didn't do anything, so PHP 5.6.16 introduced yet another\n\t\t\t\t// constant ...\n\t\t\t\t//\n\t\t\t\t// https://secure.php.net/ChangeLog-5.php#5.6.16\n\t\t\t\t// https://bugs.php.net/bug.php?id=68344\n\t\t\t\telseif (defined('MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT'))\n\t\t\t\t{\n\t\t\t\t\t$client_flags |= MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( ! empty($ssl))\n\t\t\t{\n\t\t\t\t$client_flags |= MYSQLI_CLIENT_SSL;\n\t\t\t\t$this->_mysqli->ssl_set(\n\t\t\t\t\tisset($ssl['key'])    ? $ssl['key']    : NULL,\n\t\t\t\t\tisset($ssl['cert'])   ? $ssl['cert']   : NULL,\n\t\t\t\t\tisset($ssl['ca'])     ? $ssl['ca']     : NULL,\n\t\t\t\t\tisset($ssl['capath']) ? $ssl['capath'] : NULL,\n\t\t\t\t\tisset($ssl['cipher']) ? $ssl['cipher'] : NULL\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif ($this->_mysqli->real_connect($hostname, $this->username, $this->password, $this->database, $port, $socket, $client_flags))\n\t\t{\n\t\t\t// Prior to version 5.7.3, MySQL silently downgrades to an unencrypted connection if SSL setup fails\n\t\t\tif (\n\t\t\t\t($client_flags & MYSQLI_CLIENT_SSL)\n\t\t\t\t&& version_compare($this->_mysqli->client_info, '5.7.3', '<=')\n\t\t\t\t&& empty($this->_mysqli->query(\"SHOW STATUS LIKE 'ssl_cipher'\")->fetch_object()->Value)\n\t\t\t)\n\t\t\t{\n\t\t\t\t$this->_mysqli->close();\n\t\t\t\t$message = 'MySQLi was configured for an SSL connection, but got an unencrypted connection instead!';\n\t\t\t\tlog_message('error', $message);\n\t\t\t\treturn ($this->db_debug) ? $this->display_error($message, '', TRUE) : FALSE;\n\t\t\t}\n\n\t\t\tif ( ! $this->_mysqli->set_charset($this->char_set))\n\t\t\t{\n\t\t\t\tlog_message('error', \"Database: Unable to set the configured connection charset ('{$this->char_set}').\");\n\t\t\t\t$this->_mysqli->close();\n\t\t\t\treturn ($this->db->db_debug) ? $this->display_error('db_unable_to_set_charset', $this->char_set) : FALSE;\n\t\t\t}\n\n\t\t\treturn $this->_mysqli;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Reconnect\n\t *\n\t * Keep / reestablish the db connection if no queries have been\n\t * sent for a length of time exceeding the server's idle timeout\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function reconnect()\n\t{\n\t\tif ($this->conn_id !== FALSE && $this->conn_id->ping() === FALSE)\n\t\t{\n\t\t\t$this->conn_id = FALSE;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Select the database\n\t *\n\t * @param\tstring\t$database\n\t * @return\tbool\n\t */\n\tpublic function db_select($database = '')\n\t{\n\t\tif ($database === '')\n\t\t{\n\t\t\t$database = $this->database;\n\t\t}\n\n\t\tif ($this->conn_id->select_db($database))\n\t\t{\n\t\t\t$this->database = $database;\n\t\t\t$this->data_cache = array();\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Database version number\n\t *\n\t * @return\tstring\n\t */\n\tpublic function version()\n\t{\n\t\tif (isset($this->data_cache['version']))\n\t\t{\n\t\t\treturn $this->data_cache['version'];\n\t\t}\n\n\t\treturn $this->data_cache['version'] = $this->conn_id->server_info;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Execute the query\n\t *\n\t * @param\tstring\t$sql\tan SQL query\n\t * @return\tmixed\n\t */\n\tprotected function _execute($sql)\n\t{\n\t\treturn $this->conn_id->query($this->_prep_query($sql));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Prep the query\n\t *\n\t * If needed, each database adapter can prep the query string\n\t *\n\t * @param\tstring\t$sql\tan SQL query\n\t * @return\tstring\n\t */\n\tprotected function _prep_query($sql)\n\t{\n\t\t// mysqli_affected_rows() returns 0 for \"DELETE FROM TABLE\" queries. This hack\n\t\t// modifies the query so that it a proper number of affected rows is returned.\n\t\tif ($this->delete_hack === TRUE && preg_match('/^\\s*DELETE\\s+FROM\\s+(\\S+)\\s*$/i', $sql))\n\t\t{\n\t\t\treturn trim($sql).' WHERE 1=1';\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Begin Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_begin()\n\t{\n\t\t$this->conn_id->autocommit(FALSE);\n\t\treturn is_php('5.5')\n\t\t\t? $this->conn_id->begin_transaction()\n\t\t\t: $this->simple_query('START TRANSACTION'); // can also be BEGIN or BEGIN WORK\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Commit Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_commit()\n\t{\n\t\tif ($this->conn_id->commit())\n\t\t{\n\t\t\t$this->conn_id->autocommit(TRUE);\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Rollback Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_rollback()\n\t{\n\t\tif ($this->conn_id->rollback())\n\t\t{\n\t\t\t$this->conn_id->autocommit(TRUE);\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Platform-dependent string escape\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tprotected function _escape_str($str)\n\t{\n\t\treturn $this->conn_id->real_escape_string($str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Affected Rows\n\t *\n\t * @return\tint\n\t */\n\tpublic function affected_rows()\n\t{\n\t\treturn $this->conn_id->affected_rows;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert ID\n\t *\n\t * @return\tint\n\t */\n\tpublic function insert_id()\n\t{\n\t\treturn $this->conn_id->insert_id;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * List table query\n\t *\n\t * Generates a platform-specific query string so that the table names can be fetched\n\t *\n\t * @param\tbool\t$prefix_limit\n\t * @return\tstring\n\t */\n\tprotected function _list_tables($prefix_limit = FALSE)\n\t{\n\t\t$sql = 'SHOW TABLES FROM '.$this->_escape_char.$this->database.$this->_escape_char;\n\n\t\tif ($prefix_limit !== FALSE && $this->dbprefix !== '')\n\t\t{\n\t\t\treturn $sql.\" LIKE '\".$this->escape_like_str($this->dbprefix).\"%'\";\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show column query\n\t *\n\t * Generates a platform-specific query string so that the column names can be fetched\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _list_columns($table = '')\n\t{\n\t\treturn 'SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns an object with field data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tarray\n\t */\n\tpublic function field_data($table)\n\t{\n\t\tif (($query = $this->query('SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE))) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\t\t$query = $query->result_object();\n\n\t\t$retval = array();\n\t\tfor ($i = 0, $c = count($query); $i < $c; $i++)\n\t\t{\n\t\t\t$retval[$i]\t\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t\t= $query[$i]->Field;\n\n\t\t\tsscanf($query[$i]->Type, '%[a-z](%d)',\n\t\t\t\t$retval[$i]->type,\n\t\t\t\t$retval[$i]->max_length\n\t\t\t);\n\n\t\t\t$retval[$i]->default\t\t= $query[$i]->Default;\n\t\t\t$retval[$i]->primary_key\t= (int) ($query[$i]->Key === 'PRI');\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Error\n\t *\n\t * Returns an array containing code and message of the last\n\t * database error that has occurred.\n\t *\n\t * @return\tarray\n\t */\n\tpublic function error()\n\t{\n\t\tif ( ! empty($this->_mysqli->connect_errno))\n\t\t{\n\t\t\treturn array(\n\t\t\t\t'code'    => $this->_mysqli->connect_errno,\n\t\t\t\t'message' => $this->_mysqli->connect_error\n\t\t\t);\n\t\t}\n\n\t\treturn array('code' => $this->conn_id->errno, 'message' => $this->conn_id->error);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * FROM tables\n\t *\n\t * Groups tables in FROM clauses if needed, so there is no confusion\n\t * about operator precedence.\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _from_tables()\n\t{\n\t\tif ( ! empty($this->qb_join) && count($this->qb_from) > 1)\n\t\t{\n\t\t\treturn '('.implode(', ', $this->qb_from).')';\n\t\t}\n\n\t\treturn implode(', ', $this->qb_from);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Close DB Connection\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _close()\n\t{\n\t\t$this->conn_id->close();\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/mysqli/mysqli_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.3.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * MySQLi Forge Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_mysqli_forge extends CI_DB_forge {\n\n\t/**\n\t * CREATE DATABASE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_database\t= 'CREATE DATABASE %s CHARACTER SET %s COLLATE %s';\n\n\t/**\n\t * CREATE TABLE keys flag\n\t *\n\t * Whether table keys are created from within the\n\t * CREATE TABLE statement.\n\t *\n\t * @var\tbool\n\t */\n\tprotected $_create_table_keys\t= TRUE;\n\n\t/**\n\t * UNSIGNED support\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_unsigned\t\t= array(\n\t\t'TINYINT',\n\t\t'SMALLINT',\n\t\t'MEDIUMINT',\n\t\t'INT',\n\t\t'INTEGER',\n\t\t'BIGINT',\n\t\t'REAL',\n\t\t'DOUBLE',\n\t\t'DOUBLE PRECISION',\n\t\t'FLOAT',\n\t\t'DECIMAL',\n\t\t'NUMERIC'\n\t);\n\n\t/**\n\t * NULL value representation in CREATE/ALTER TABLE statements\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_null = 'NULL';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * CREATE TABLE attributes\n\t *\n\t * @param\tarray\t$attributes\tAssociative array of table attributes\n\t * @return\tstring\n\t */\n\tprotected function _create_table_attr($attributes)\n\t{\n\t\t$sql = '';\n\n\t\tforeach (array_keys($attributes) as $key)\n\t\t{\n\t\t\tif (is_string($key))\n\t\t\t{\n\t\t\t\t$sql .= ' '.strtoupper($key).' = '.$attributes[$key];\n\t\t\t}\n\t\t}\n\n\t\tif ( ! empty($this->db->char_set) && ! strpos($sql, 'CHARACTER SET') && ! strpos($sql, 'CHARSET'))\n\t\t{\n\t\t\t$sql .= ' DEFAULT CHARACTER SET = '.$this->db->char_set;\n\t\t}\n\n\t\tif ( ! empty($this->db->dbcollat) && ! strpos($sql, 'COLLATE'))\n\t\t{\n\t\t\t$sql .= ' COLLATE = '.$this->db->dbcollat;\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ALTER TABLE\n\t *\n\t * @param\tstring\t$alter_type\tALTER type\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tmixed\t$field\t\tColumn definition\n\t * @return\tstring|string[]\n\t */\n\tprotected function _alter_table($alter_type, $table, $field)\n\t{\n\t\tif ($alter_type === 'DROP')\n\t\t{\n\t\t\treturn parent::_alter_table($alter_type, $table, $field);\n\t\t}\n\n\t\t$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table);\n\t\tfor ($i = 0, $c = count($field); $i < $c; $i++)\n\t\t{\n\t\t\tif ($field[$i]['_literal'] !== FALSE)\n\t\t\t{\n\t\t\t\t$field[$i] = ($alter_type === 'ADD')\n\t\t\t\t\t\t? \"\\n\\tADD \".$field[$i]['_literal']\n\t\t\t\t\t\t: \"\\n\\tMODIFY \".$field[$i]['_literal'];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif ($alter_type === 'ADD')\n\t\t\t\t{\n\t\t\t\t\t$field[$i]['_literal'] = \"\\n\\tADD \";\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$field[$i]['_literal'] = empty($field[$i]['new_name']) ? \"\\n\\tMODIFY \" : \"\\n\\tCHANGE \";\n\t\t\t\t}\n\n\t\t\t\t$field[$i] = $field[$i]['_literal'].$this->_process_column($field[$i]);\n\t\t\t}\n\t\t}\n\n\t\treturn array($sql.implode(',', $field));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Process column\n\t *\n\t * @param\tarray\t$field\n\t * @return\tstring\n\t */\n\tprotected function _process_column($field)\n\t{\n\t\t$extra_clause = isset($field['after'])\n\t\t\t? ' AFTER '.$this->db->escape_identifiers($field['after']) : '';\n\n\t\tif (empty($extra_clause) && isset($field['first']) && $field['first'] === TRUE)\n\t\t{\n\t\t\t$extra_clause = ' FIRST';\n\t\t}\n\n\t\treturn $this->db->escape_identifiers($field['name'])\n\t\t\t.(empty($field['new_name']) ? '' : ' '.$this->db->escape_identifiers($field['new_name']))\n\t\t\t.' '.$field['type'].$field['length']\n\t\t\t.$field['unsigned']\n\t\t\t.$field['null']\n\t\t\t.$field['default']\n\t\t\t.$field['auto_increment']\n\t\t\t.$field['unique']\n\t\t\t.(empty($field['comment']) ? '' : ' COMMENT '.$field['comment'])\n\t\t\t.$extra_clause;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Process indexes\n\t *\n\t * @param\tstring\t$table\t(ignored)\n\t * @return\tstring\n\t */\n\tprotected function _process_indexes($table)\n\t{\n\t\t$sql = '';\n\n\t\tfor ($i = 0, $c = count($this->keys); $i < $c; $i++)\n\t\t{\n\t\t\tif (is_array($this->keys[$i]))\n\t\t\t{\n\t\t\t\tfor ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++)\n\t\t\t\t{\n\t\t\t\t\tif ( ! isset($this->fields[$this->keys[$i][$i2]]))\n\t\t\t\t\t{\n\t\t\t\t\t\tunset($this->keys[$i][$i2]);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telseif ( ! isset($this->fields[$this->keys[$i]]))\n\t\t\t{\n\t\t\t\tunset($this->keys[$i]);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tis_array($this->keys[$i]) OR $this->keys[$i] = array($this->keys[$i]);\n\n\t\t\t$sql .= \",\\n\\tKEY \".$this->db->escape_identifiers(implode('_', $this->keys[$i]))\n\t\t\t\t.' ('.implode(', ', $this->db->escape_identifiers($this->keys[$i])).')';\n\t\t}\n\n\t\t$this->keys = array();\n\n\t\treturn $sql;\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/mysqli/mysqli_result.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.3.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * MySQLi Result Class\n *\n * This class extends the parent result class: CI_DB_result\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_mysqli_result extends CI_DB_result {\n\n\t/**\n\t * Number of rows in the result set\n\t *\n\t * @return\tint\n\t */\n\tpublic function num_rows()\n\t{\n\t\treturn is_int($this->num_rows)\n\t\t\t? $this->num_rows\n\t\t\t: $this->num_rows = $this->result_id->num_rows;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Number of fields in the result set\n\t *\n\t * @return\tint\n\t */\n\tpublic function num_fields()\n\t{\n\t\treturn $this->result_id->field_count;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch Field Names\n\t *\n\t * Generates an array of column names\n\t *\n\t * @return\tarray\n\t */\n\tpublic function list_fields()\n\t{\n\t\t$field_names = array();\n\t\t$this->result_id->field_seek(0);\n\t\twhile ($field = $this->result_id->fetch_field())\n\t\t{\n\t\t\t$field_names[] = $field->name;\n\t\t}\n\n\t\treturn $field_names;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field data\n\t *\n\t * Generates an array of objects containing field meta-data\n\t *\n\t * @return\tarray\n\t */\n\tpublic function field_data()\n\t{\n\t\t$retval = array();\n\t\t$field_data = $this->result_id->fetch_fields();\n\t\tfor ($i = 0, $c = count($field_data); $i < $c; $i++)\n\t\t{\n\t\t\t$retval[$i]\t\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t\t= $field_data[$i]->name;\n\t\t\t$retval[$i]->type\t\t= static::_get_field_type($field_data[$i]->type);\n\t\t\t$retval[$i]->max_length\t\t= $field_data[$i]->max_length;\n\t\t\t$retval[$i]->primary_key\t= (int) ($field_data[$i]->flags & MYSQLI_PRI_KEY_FLAG);\n\t\t\t$retval[$i]->default\t\t= $field_data[$i]->def;\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get field type\n\t *\n\t * Extracts field type info from the bitflags returned by\n\t * mysqli_result::fetch_fields()\n\t *\n\t * @used-by\tCI_DB_mysqli_result::field_data()\n\t * @param\tint\t$type\n\t * @return\tstring\n\t */\n\tprivate static function _get_field_type($type)\n\t{\n\t\tstatic $map;\n\t\tisset($map) OR $map = array(\n\t\t\tMYSQLI_TYPE_DECIMAL     => 'decimal',\n\t\t\tMYSQLI_TYPE_BIT         => 'bit',\n\t\t\tMYSQLI_TYPE_TINY        => 'tinyint',\n\t\t\tMYSQLI_TYPE_SHORT       => 'smallint',\n\t\t\tMYSQLI_TYPE_INT24       => 'mediumint',\n\t\t\tMYSQLI_TYPE_LONG        => 'int',\n\t\t\tMYSQLI_TYPE_LONGLONG    => 'bigint',\n\t\t\tMYSQLI_TYPE_FLOAT       => 'float',\n\t\t\tMYSQLI_TYPE_DOUBLE      => 'double',\n\t\t\tMYSQLI_TYPE_TIMESTAMP   => 'timestamp',\n\t\t\tMYSQLI_TYPE_DATE        => 'date',\n\t\t\tMYSQLI_TYPE_TIME        => 'time',\n\t\t\tMYSQLI_TYPE_DATETIME    => 'datetime',\n\t\t\tMYSQLI_TYPE_YEAR        => 'year',\n\t\t\tMYSQLI_TYPE_NEWDATE     => 'date',\n\t\t\tMYSQLI_TYPE_INTERVAL    => 'interval',\n\t\t\tMYSQLI_TYPE_ENUM        => 'enum',\n\t\t\tMYSQLI_TYPE_SET         => 'set',\n\t\t\tMYSQLI_TYPE_TINY_BLOB   => 'tinyblob',\n\t\t\tMYSQLI_TYPE_MEDIUM_BLOB => 'mediumblob',\n\t\t\tMYSQLI_TYPE_BLOB        => 'blob',\n\t\t\tMYSQLI_TYPE_LONG_BLOB   => 'longblob',\n\t\t\tMYSQLI_TYPE_STRING      => 'char',\n\t\t\tMYSQLI_TYPE_VAR_STRING  => 'varchar',\n\t\t\tMYSQLI_TYPE_GEOMETRY    => 'geometry'\n\t\t);\n\n\t\treturn isset($map[$type]) ? $map[$type] : $type;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Free the result\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function free_result()\n\t{\n\t\tif (is_object($this->result_id))\n\t\t{\n\t\t\t$this->result_id->free();\n\t\t\t$this->result_id = FALSE;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Data Seek\n\t *\n\t * Moves the internal pointer to the desired offset. We call\n\t * this internally before fetching results to make sure the\n\t * result set starts at zero.\n\t *\n\t * @param\tint\t$n\n\t * @return\tbool\n\t */\n\tpublic function data_seek($n = 0)\n\t{\n\t\treturn $this->result_id->data_seek($n);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - associative array\n\t *\n\t * Returns the result set as an array\n\t *\n\t * @return\tarray\n\t */\n\tprotected function _fetch_assoc()\n\t{\n\t\treturn $this->result_id->fetch_assoc();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - object\n\t *\n\t * Returns the result set as an object\n\t *\n\t * @param\tstring\t$class_name\n\t * @return\tobject\n\t */\n\tprotected function _fetch_object($class_name = 'stdClass')\n\t{\n\t\treturn $this->result_id->fetch_object($class_name);\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/mysqli/mysqli_utility.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.3.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * MySQLi Utility Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_mysqli_utility extends CI_DB_utility {\n\n\t/**\n\t * List databases statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_list_databases\t= 'SHOW DATABASES';\n\n\t/**\n\t * OPTIMIZE TABLE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_optimize_table\t= 'OPTIMIZE TABLE %s';\n\n\t/**\n\t * REPAIR TABLE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_repair_table\t= 'REPAIR TABLE %s';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Export\n\t *\n\t * @param\tarray\t$params\tPreferences\n\t * @return\tmixed\n\t */\n\tprotected function _backup($params = array())\n\t{\n\t\tif (count($params) === 0)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Extract the prefs for simplicity\n\t\textract($params);\n\n\t\t// Build the output\n\t\t$output = '';\n\n\t\t// Do we need to include a statement to disable foreign key checks?\n\t\tif ($foreign_key_checks === FALSE)\n\t\t{\n\t\t\t$output .= 'SET foreign_key_checks = 0;'.$newline;\n\t\t}\n\n\t\tforeach ( (array) $tables as $table)\n\t\t{\n\t\t\t// Is the table in the \"ignore\" list?\n\t\t\tif (in_array($table, (array) $ignore, TRUE))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Get the table schema\n\t\t\t$query = $this->db->query('SHOW CREATE TABLE '.$this->db->escape_identifiers($this->db->database.'.'.$table));\n\n\t\t\t// No result means the table name was invalid\n\t\t\tif ($query === FALSE)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Write out the table schema\n\t\t\t$output .= '#'.$newline.'# TABLE STRUCTURE FOR: '.$table.$newline.'#'.$newline.$newline;\n\n\t\t\tif ($add_drop === TRUE)\n\t\t\t{\n\t\t\t\t$output .= 'DROP TABLE IF EXISTS '.$this->db->protect_identifiers($table).';'.$newline.$newline;\n\t\t\t}\n\n\t\t\t$i = 0;\n\t\t\t$result = $query->result_array();\n\t\t\tforeach ($result[0] as $val)\n\t\t\t{\n\t\t\t\tif ($i++ % 2)\n\t\t\t\t{\n\t\t\t\t\t$output .= $val.';'.$newline.$newline;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If inserts are not needed we're done...\n\t\t\tif ($add_insert === FALSE)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Grab all the data from the current table\n\t\t\t$query = $this->db->query('SELECT * FROM '.$this->db->protect_identifiers($table));\n\n\t\t\tif ($query->num_rows() === 0)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Fetch the field names and determine if the field is an\n\t\t\t// integer type. We use this info to decide whether to\n\t\t\t// surround the data with quotes or not\n\n\t\t\t$i = 0;\n\t\t\t$field_str = '';\n\t\t\t$is_int = array();\n\t\t\twhile ($field = $query->result_id->fetch_field())\n\t\t\t{\n\t\t\t\t// Most versions of MySQL store timestamp as a string\n\t\t\t\t$is_int[$i] = in_array($field->type, array(MYSQLI_TYPE_TINY, MYSQLI_TYPE_SHORT, MYSQLI_TYPE_INT24, MYSQLI_TYPE_LONG), TRUE);\n\n\t\t\t\t// Create a string of field names\n\t\t\t\t$field_str .= $this->db->escape_identifiers($field->name).', ';\n\t\t\t\t$i++;\n\t\t\t}\n\n\t\t\t// Trim off the end comma\n\t\t\t$field_str = preg_replace('/, $/' , '', $field_str);\n\n\t\t\t// Build the insert string\n\t\t\tforeach ($query->result_array() as $row)\n\t\t\t{\n\t\t\t\t$val_str = '';\n\n\t\t\t\t$i = 0;\n\t\t\t\tforeach ($row as $v)\n\t\t\t\t{\n\t\t\t\t\t// Is the value NULL?\n\t\t\t\t\tif ($v === NULL)\n\t\t\t\t\t{\n\t\t\t\t\t\t$val_str .= 'NULL';\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// Escape the data if it's not an integer\n\t\t\t\t\t\t$val_str .= ($is_int[$i] === FALSE) ? $this->db->escape($v) : $v;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Append a comma\n\t\t\t\t\t$val_str .= ', ';\n\t\t\t\t\t$i++;\n\t\t\t\t}\n\n\t\t\t\t// Remove the comma at the end of the string\n\t\t\t\t$val_str = preg_replace('/, $/' , '', $val_str);\n\n\t\t\t\t// Build the INSERT string\n\t\t\t\t$output .= 'INSERT INTO '.$this->db->protect_identifiers($table).' ('.$field_str.') VALUES ('.$val_str.');'.$newline;\n\t\t\t}\n\n\t\t\t$output .= $newline.$newline;\n\t\t}\n\n\t\t// Do we need to include a statement to re-enable foreign key checks?\n\t\tif ($foreign_key_checks === FALSE)\n\t\t{\n\t\t\t$output .= 'SET foreign_key_checks = 1;'.$newline;\n\t\t}\n\n\t\treturn $output;\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/oci8/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/database/drivers/oci8/oci8_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.4.1\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * oci8 Database Adapter Class\n *\n * Note: _DB is an extender class that the app controller\n * creates dynamically based on whether the query builder\n * class is being used or not.\n *\n * @package\t\tCodeIgniter\n * @subpackage  Drivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\n\n/**\n * oci8 Database Adapter Class\n *\n * This is a modification of the DB_driver class to\n * permit access to oracle databases\n *\n * @author\t  Kelly McArdle\n */\nclass CI_DB_oci8_driver extends CI_DB {\n\n\t/**\n\t * Database driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $dbdriver = 'oci8';\n\n\t/**\n\t * Commit mode flag\n\t *\n\t * @var\tint\n\t */\n\tpublic $commit_mode = OCI_COMMIT_ON_SUCCESS;\n\n\t/**\n\t * Limit used flag\n\t *\n\t * If we use LIMIT, we'll add a field that will\n\t * throw off num_fields later.\n\t *\n\t * @var\tbool\n\t */\n\tpublic $limit_used = FALSE;\n\n\t/**\n\t * Error cache\n\t *\n\t * Cached error info about failed queries.\n\t * Used so that statement IDs can be released immediately.\n\t *\n\t * @var\tarray|false\n\t */\n\tprotected $_error = FALSE;\n\n\t/**\n\t * Affected rows\n\t *\n\t * Cached result of oci_num_rows().\n\t * Used so that statement IDs can be released immediately.\n\t *\n\t * @var\tint|false\n\t */\n\tprotected $_affected_rows = FALSE;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * List of reserved identifiers\n\t *\n\t * Identifiers that must NOT be escaped.\n\t *\n\t * @var\tstring[]\n\t */\n\tprotected $_reserved_identifiers = array('*', 'rownum');\n\n\t/**\n\t * ORDER BY random keyword\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_random_keyword = array('ASC', 'ASC'); // not currently supported\n\n\t/**\n\t * COUNT string\n\t *\n\t * @used-by\tCI_DB_driver::count_all()\n\t * @used-by\tCI_DB_query_builder::count_all_results()\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_count_string = 'SELECT COUNT(1) AS ';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * @param\tarray\t$params\n\t * @return\tvoid\n\t */\n\tpublic function __construct($params)\n\t{\n\t\tparent::__construct($params);\n\n\t\t$valid_dsns = array(\n\t\t\t'tns'\t=> '/^\\(DESCRIPTION=(\\(.+\\)){2,}\\)$/', // TNS\n\t\t\t// Easy Connect string (Oracle 10g+)\n\t\t\t'ec'\t=> '/^(\\/\\/)?[a-z0-9.:_-]+(:[1-9][0-9]{0,4})?(\\/[a-z0-9$_]+)?(:[^\\/])?(\\/[a-z0-9$_]+)?$/i',\n\t\t\t'in'\t=> '/^[a-z0-9$_]+$/i' // Instance name (defined in tnsnames.ora)\n\t\t);\n\n\t\t/* Space characters don't have any effect when actually\n\t\t * connecting, but can be a hassle while validating the DSN.\n\t\t */\n\t\t$this->dsn = str_replace(array(\"\\n\", \"\\r\", \"\\t\", ' '), '', $this->dsn);\n\n\t\tif ($this->dsn !== '')\n\t\t{\n\t\t\tforeach ($valid_dsns as $regexp)\n\t\t\t{\n\t\t\t\tif (preg_match($regexp, $this->dsn))\n\t\t\t\t{\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Legacy support for TNS in the hostname configuration field\n\t\t$this->hostname = str_replace(array(\"\\n\", \"\\r\", \"\\t\", ' '), '', $this->hostname);\n\t\tif (preg_match($valid_dsns['tns'], $this->hostname))\n\t\t{\n\t\t\t$this->dsn = $this->hostname;\n\t\t\treturn;\n\t\t}\n\t\telseif ($this->hostname !== '' && strpos($this->hostname, '/') === FALSE && strpos($this->hostname, ':') === FALSE\n\t\t\t&& (( ! empty($this->port) && ctype_digit($this->port)) OR $this->database !== ''))\n\t\t{\n\t\t\t/* If the hostname field isn't empty, doesn't contain\n\t\t\t * ':' and/or '/' and if port and/or database aren't\n\t\t\t * empty, then the hostname field is most likely indeed\n\t\t\t * just a hostname. Therefore we'll try and build an\n\t\t\t * Easy Connect string from these 3 settings, assuming\n\t\t\t * that the database field is a service name.\n\t\t\t */\n\t\t\t$this->dsn = $this->hostname\n\t\t\t\t.(( ! empty($this->port) && ctype_digit($this->port)) ? ':'.$this->port : '')\n\t\t\t\t.($this->database !== '' ? '/'.ltrim($this->database, '/') : '');\n\n\t\t\tif (preg_match($valid_dsns['ec'], $this->dsn))\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t/* At this point, we can only try and validate the hostname and\n\t\t * database fields separately as DSNs.\n\t\t */\n\t\tif (preg_match($valid_dsns['ec'], $this->hostname) OR preg_match($valid_dsns['in'], $this->hostname))\n\t\t{\n\t\t\t$this->dsn = $this->hostname;\n\t\t\treturn;\n\t\t}\n\n\t\t$this->database = str_replace(array(\"\\n\", \"\\r\", \"\\t\", ' '), '', $this->database);\n\t\tforeach ($valid_dsns as $regexp)\n\t\t{\n\t\t\tif (preg_match($regexp, $this->database))\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t/* Well - OK, an empty string should work as well.\n\t\t * PHP will try to use environment variables to\n\t\t * determine which Oracle instance to connect to.\n\t\t */\n\t\t$this->dsn = '';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Non-persistent database connection\n\t *\n\t * @param\tbool\t$persistent\n\t * @return\tresource\n\t */\n\tpublic function db_connect($persistent = FALSE)\n\t{\n\t\t$func = ($persistent === TRUE) ? 'oci_pconnect' : 'oci_connect';\n\t\treturn empty($this->char_set)\n\t\t\t? $func($this->username, $this->password, $this->dsn)\n\t\t\t: $func($this->username, $this->password, $this->dsn, $this->char_set);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Database version number\n\t *\n\t * @return\tstring\n\t */\n\tpublic function version()\n\t{\n\t\tif (isset($this->data_cache['version']))\n\t\t{\n\t\t\treturn $this->data_cache['version'];\n\t\t}\n\n\t\tif ( ! $this->conn_id OR ($version_string = oci_server_version($this->conn_id)) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\t\telseif (preg_match('#Release\\s(\\d+(?:\\.\\d+)+)#', $version_string, $match))\n\t\t{\n\t\t\treturn $this->data_cache['version'] = $match[1];\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Execute the query\n\t *\n\t * @param\tstring\t$sql\tan SQL query\n\t * @return\tresource\n\t */\n\tprotected function _execute($sql)\n\t{\n\t\t/* Oracle must parse the query before it is run. All of the actions with\n\t\t * the query are based on the statement id returned by oci_parse().\n\t\t */\n\t\t$this->result_id = oci_parse($this->conn_id, $sql);\n\t\toci_set_prefetch($this->result_id, 1000);\n\t\t$result = oci_execute($this->result_id, $this->commit_mode);\n\t\t$this->_error = oci_error($this->result_id);\n\t\t$this->is_write_type($sql) && $this->_affected_rows = oci_num_rows($this->result_id);\n\n\t\tif ($this->is_write_type($sql) OR $result === FALSE)\n\t\t{\n\t\t\toci_free_statement($this->result_id);\n\t\t\treturn $result;\n\t\t}\n\n\t\treturn $this->result_id;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Begin Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_begin()\n\t{\n\t\t$this->commit_mode = OCI_NO_AUTO_COMMIT;\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Commit Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_commit()\n\t{\n\t\t$this->commit_mode = OCI_COMMIT_ON_SUCCESS;\n\n\t\treturn oci_commit($this->conn_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Rollback Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_rollback()\n\t{\n\t\t$this->commit_mode = OCI_COMMIT_ON_SUCCESS;\n\t\treturn oci_rollback($this->conn_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Affected Rows\n\t *\n\t * @return\tint\n\t */\n\tpublic function affected_rows()\n\t{\n\t\treturn $this->_affected_rows;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert ID\n\t *\n\t * @return\tint\n\t */\n\tpublic function insert_id()\n\t{\n\t\t// not supported in oracle\n\t\treturn $this->display_error('db_unsupported_function');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show table query\n\t *\n\t * Generates a platform-specific query string so that the table names can be fetched\n\t *\n\t * @param\tbool\t$prefix_limit\n\t * @return\tstring\n\t */\n\tprotected function _list_tables($prefix_limit = FALSE)\n\t{\n\t\t$sql = 'SELECT \"TABLE_NAME\" FROM \"ALL_TABLES\"';\n\n\t\tif ($prefix_limit !== FALSE && $this->dbprefix !== '')\n\t\t{\n\t\t\treturn $sql.' WHERE \"TABLE_NAME\" LIKE \\''.$this->escape_like_str($this->dbprefix).\"%' \"\n\t\t\t\t.sprintf($this->_like_escape_str, $this->_like_escape_chr);\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show column query\n\t *\n\t * Generates a platform-specific query string so that the column names can be fetched\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _list_columns($table = '')\n\t{\n\t\tif (strpos($table, '.') !== FALSE)\n\t\t{\n\t\t\tsscanf($table, '%[^.].%s', $owner, $table);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$owner = $this->username;\n\t\t}\n\n\t\treturn 'SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS\n\t\t\tWHERE UPPER(OWNER) = '.$this->escape(strtoupper($owner)).'\n\t\t\t\tAND UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns an object with field data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tarray\n\t */\n\tpublic function field_data($table)\n\t{\n\t\tif (strpos($table, '.') !== FALSE)\n\t\t{\n\t\t\tsscanf($table, '%[^.].%s', $owner, $table);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$owner = $this->username;\n\t\t}\n\n\t\t$sql = 'SELECT COLUMN_NAME, DATA_TYPE, CHAR_LENGTH, DATA_PRECISION, DATA_LENGTH, DATA_DEFAULT, NULLABLE\n\t\t\tFROM ALL_TAB_COLUMNS\n\t\t\tWHERE UPPER(OWNER) = '.$this->escape(strtoupper($owner)).'\n\t\t\t\tAND UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));\n\n\t\tif (($query = $this->query($sql)) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\t\t$query = $query->result_object();\n\n\t\t$retval = array();\n\t\tfor ($i = 0, $c = count($query); $i < $c; $i++)\n\t\t{\n\t\t\t$retval[$i]\t\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t\t= $query[$i]->COLUMN_NAME;\n\t\t\t$retval[$i]->type\t\t= $query[$i]->DATA_TYPE;\n\n\t\t\t$length = ($query[$i]->CHAR_LENGTH > 0)\n\t\t\t\t? $query[$i]->CHAR_LENGTH : $query[$i]->DATA_PRECISION;\n\t\t\tif ($length === NULL)\n\t\t\t{\n\t\t\t\t$length = $query[$i]->DATA_LENGTH;\n\t\t\t}\n\t\t\t$retval[$i]->max_length\t\t= $length;\n\n\t\t\t$default = $query[$i]->DATA_DEFAULT;\n\t\t\tif ($default === NULL && $query[$i]->NULLABLE === 'N')\n\t\t\t{\n\t\t\t\t$default = '';\n\t\t\t}\n\t\t\t$retval[$i]->default = $default;\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Error\n\t *\n\t * Returns an array containing code and message of the last\n\t * database error that has occurred.\n\t *\n\t * @return\tarray\n\t */\n\tpublic function error()\n\t{\n\t\tif ( ! empty($this->_error))\n\t\t{\n\t\t\treturn $this->_error;\n\t\t}\n\n\t\t// oci_error() returns an array that already contains\n\t\t// 'code' and 'message' keys, but it can return false\n\t\t// if there was no error ....\n\t\tif (is_resource($this->conn_id))\n\t\t{\n\t\t\t$error = oci_error($this->conn_id);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$error = oci_error();\n\t\t}\n\n\t\treturn is_array($error)\n\t\t\t? $error\n\t\t\t: array('code' => '', 'message' => '');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert batch statement\n\t *\n\t * Generates a platform-specific insert string from the supplied data\n\t *\n\t * @param\tstring\t$table\tTable name\n\t * @param\tarray\t$keys\tINSERT keys\n\t * @param \tarray\t$values\tINSERT values\n\t * @return\tstring\n\t */\n\tprotected function _insert_batch($table, $keys, $values)\n\t{\n\t\t$keys = implode(', ', $keys);\n\t\t$sql = \"INSERT ALL\\n\";\n\n\t\tfor ($i = 0, $c = count($values); $i < $c; $i++)\n\t\t{\n\t\t\t$sql .= '\tINTO '.$table.' ('.$keys.') VALUES '.$values[$i].\"\\n\";\n\t\t}\n\n\t\treturn $sql.'SELECT * FROM dual';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Truncate statement\n\t *\n\t * Generates a platform-specific truncate string from the supplied data\n\t *\n\t * If the database does not support the TRUNCATE statement,\n\t * then this method maps to 'DELETE FROM table'\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _truncate($table)\n\t{\n\t\treturn 'TRUNCATE TABLE '.$table;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Delete statement\n\t *\n\t * Generates a platform-specific delete string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _delete($table)\n\t{\n\t\tif ($this->qb_limit)\n\t\t{\n\t\t\t$this->where('rownum <= ',$this->qb_limit, FALSE);\n\t\t\t$this->qb_limit = FALSE;\n\t\t}\n\n\t\treturn parent::_delete($table);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * LIMIT\n\t *\n\t * Generates a platform-specific LIMIT clause\n\t *\n\t * @param\tstring\t$sql\tSQL Query\n\t * @return\tstring\n\t */\n\tprotected function _limit($sql)\n\t{\n\t\tif (version_compare($this->version(), '12.1', '>='))\n\t\t{\n\t\t\t// OFFSET-FETCH can be used only with the ORDER BY clause\n\t\t\tempty($this->qb_orderby) && $sql .= ' ORDER BY 1';\n\n\t\t\treturn $sql.' OFFSET '.(int) $this->qb_offset.' ROWS FETCH NEXT '.$this->qb_limit.' ROWS ONLY';\n\t\t}\n\n\t\t$this->limit_used = TRUE;\n\t\treturn 'SELECT * FROM (SELECT inner_query.*, rownum rnum FROM ('.$sql.') inner_query WHERE rownum < '.($this->qb_offset + $this->qb_limit + 1).')'\n\t\t\t.($this->qb_offset ? ' WHERE rnum >= '.($this->qb_offset + 1) : '');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Close DB Connection\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _close()\n\t{\n\t\tif (is_resource($this->result_id))\n\t\t{\n\t\t\toci_free_statement($this->result_id);\n\t\t}\n\n\t\toci_close($this->conn_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * We need to reset our $limit_used hack flag, so it doesn't propagate\n\t * to subsequent queries.\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _reset_select()\n\t{\n\t\t$this->limit_used = FALSE;\n\t\tparent::_reset_select();\n\t}\n}\n"
  },
  {
    "path": "system/database/drivers/oci8/oci8_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.4.1\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Oracle Forge Class\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_oci8_forge extends CI_DB_forge {\n\n\t/**\n\t * CREATE DATABASE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_database\t= FALSE;\n\n\t/**\n\t * CREATE TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_table_if\t= FALSE;\n\n\t/**\n\t * DROP DATABASE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_drop_database\t= FALSE;\n\n\t/**\n\t * DROP TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_drop_table_if\t= FALSE;\n\n\t/**\n\t * UNSIGNED support\n\t *\n\t * @var\tbool|array\n\t */\n\tprotected $_unsigned\t\t= FALSE;\n\n\t/**\n\t * NULL value representation in CREATE/ALTER TABLE statements\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_null\t\t= 'NULL';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ALTER TABLE\n\t *\n\t * @param\tstring\t$alter_type\tALTER type\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tmixed\t$field\t\tColumn definition\n\t * @return\tstring|string[]\n\t */\n\tprotected function _alter_table($alter_type, $table, $field)\n\t{\n\t\tif ($alter_type === 'DROP')\n\t\t{\n\t\t\treturn parent::_alter_table($alter_type, $table, $field);\n\t\t}\n\t\telseif ($alter_type === 'CHANGE')\n\t\t{\n\t\t\t$alter_type = 'MODIFY';\n\t\t}\n\n\t\t$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table);\n\t\t$sqls = array();\n\t\tfor ($i = 0, $c = count($field); $i < $c; $i++)\n\t\t{\n\t\t\tif ($field[$i]['_literal'] !== FALSE)\n\t\t\t{\n\t\t\t\t$field[$i] = \"\\n\\t\".$field[$i]['_literal'];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$field[$i]['_literal'] = \"\\n\\t\".$this->_process_column($field[$i]);\n\n\t\t\t\tif ( ! empty($field[$i]['comment']))\n\t\t\t\t{\n\t\t\t\t\t$sqls[] = 'COMMENT ON COLUMN '\n\t\t\t\t\t\t.$this->db->escape_identifiers($table).'.'.$this->db->escape_identifiers($field[$i]['name'])\n\t\t\t\t\t\t.' IS '.$field[$i]['comment'];\n\t\t\t\t}\n\n\t\t\t\tif ($alter_type === 'MODIFY' && ! empty($field[$i]['new_name']))\n\t\t\t\t{\n\t\t\t\t\t$sqls[] = $sql.' RENAME COLUMN '.$this->db->escape_identifiers($field[$i]['name'])\n\t\t\t\t\t\t.' TO '.$this->db->escape_identifiers($field[$i]['new_name']);\n\t\t\t\t}\n\n\t\t\t\t$field[$i] = \"\\n\\t\".$field[$i]['_literal'];\n\t\t\t}\n\t\t}\n\n\t\t$sql .= ' '.$alter_type.' ';\n\t\t$sql .= (count($field) === 1)\n\t\t\t\t? $field[0]\n\t\t\t\t: '('.implode(',', $field).')';\n\n\t\t// RENAME COLUMN must be executed after MODIFY\n\t\tarray_unshift($sqls, $sql);\n\t\treturn $sqls;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute AUTO_INCREMENT\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_auto_increment(&$attributes, &$field)\n\t{\n\t\tif ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'number') !== FALSE && version_compare($this->db->version(), '12.1', '>='))\n\t\t{\n\t\t\t$field['auto_increment'] = ' GENERATED ALWAYS AS IDENTITY';\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Process column\n\t *\n\t * @param\tarray\t$field\n\t * @return\tstring\n\t */\n\tprotected function _process_column($field)\n\t{\n\t\treturn $this->db->escape_identifiers($field['name'])\n\t\t\t.' '.$field['type'].$field['length']\n\t\t\t.$field['unsigned']\n\t\t\t.$field['default']\n\t\t\t.$field['auto_increment']\n\t\t\t.$field['null']\n\t\t\t.$field['unique'];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute TYPE\n\t *\n\t * Performs a data type mapping between different databases.\n\t *\n\t * @param\tarray\t&$attributes\n\t * @return\tvoid\n\t */\n\tprotected function _attr_type(&$attributes)\n\t{\n\t\tswitch (strtoupper($attributes['TYPE']))\n\t\t{\n\t\t\tcase 'TINYINT':\n\t\t\t\t$attributes['TYPE'] = 'NUMBER';\n\t\t\t\treturn;\n\t\t\tcase 'MEDIUMINT':\n\t\t\t\t$attributes['TYPE'] = 'NUMBER';\n\t\t\t\treturn;\n\t\t\tcase 'INT':\n\t\t\t\t$attributes['TYPE'] = 'NUMBER';\n\t\t\t\treturn;\n\t\t\tcase 'BIGINT':\n\t\t\t\t$attributes['TYPE'] = 'NUMBER';\n\t\t\t\treturn;\n\t\t\tdefault: return;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "system/database/drivers/oci8/oci8_result.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.4.1\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * oci8 Result Class\n *\n * This class extends the parent result class: CI_DB_result\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_oci8_result extends CI_DB_result {\n\n\t/**\n\t * Limit used flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $limit_used;\n\n\t/**\n\t * Commit mode flag\n\t *\n\t * @var\tint\n\t */\n\tpublic $commit_mode;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * @param\tobject\t&$driver_object\n\t * @return\tvoid\n\t */\n\tpublic function __construct(&$driver_object)\n\t{\n\t\tparent::__construct($driver_object);\n\n\t\t$this->result_id = $driver_object->result_id;\n\t\t$this->limit_used = $driver_object->limit_used;\n\t\t$this->commit_mode =& $driver_object->commit_mode;\n\t\t$driver_object->result_id = FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Number of fields in the result set\n\t *\n\t * @return\tint\n\t */\n\tpublic function num_fields()\n\t{\n\t\t$count = oci_num_fields($this->result_id);\n\n\t\t// if we used a limit we subtract it\n\t\treturn ($this->limit_used) ? $count - 1 : $count;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch Field Names\n\t *\n\t * Generates an array of column names\n\t *\n\t * @return\tarray\n\t */\n\tpublic function list_fields()\n\t{\n\t\t$field_names = array();\n\t\tfor ($c = 1, $fieldCount = $this->num_fields(); $c <= $fieldCount; $c++)\n\t\t{\n\t\t\t$field_names[] = oci_field_name($this->result_id, $c);\n\t\t}\n\t\treturn $field_names;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field data\n\t *\n\t * Generates an array of objects containing field meta-data\n\t *\n\t * @return\tarray\n\t */\n\tpublic function field_data()\n\t{\n\t\t$retval = array();\n\t\tfor ($c = 1, $fieldCount = $this->num_fields(); $c <= $fieldCount; $c++)\n\t\t{\n\t\t\t$F\t\t= new stdClass();\n\t\t\t$F->name\t= oci_field_name($this->result_id, $c);\n\t\t\t$F->type\t= oci_field_type($this->result_id, $c);\n\t\t\t$F->max_length\t= oci_field_size($this->result_id, $c);\n\n\t\t\t$retval[] = $F;\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Free the result\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function free_result()\n\t{\n\t\tif (is_resource($this->result_id))\n\t\t{\n\t\t\toci_free_statement($this->result_id);\n\t\t\t$this->result_id = FALSE;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - associative array\n\t *\n\t * Returns the result set as an array\n\t *\n\t * @return\tarray\n\t */\n\tprotected function _fetch_assoc()\n\t{\n\t\treturn oci_fetch_assoc($this->result_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - object\n\t *\n\t * Returns the result set as an object\n\t *\n\t * @param\tstring\t$class_name\n\t * @return\tobject\n\t */\n\tprotected function _fetch_object($class_name = 'stdClass')\n\t{\n\t\t$row = oci_fetch_object($this->result_id);\n\n\t\tif ($class_name === 'stdClass' OR ! $row)\n\t\t{\n\t\t\treturn $row;\n\t\t}\n\n\t\t$class_name = new $class_name();\n\t\tforeach ($row as $key => $value)\n\t\t{\n\t\t\t$class_name->$key = $value;\n\t\t}\n\n\t\treturn $class_name;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Destructor\n\t *\n\t * Attempt to free remaining statement IDs.\n\t *\n\t * @see\thttps://github.com/bcit-ci/CodeIgniter/pull/5896\n\t * @return\tvoid\n\t */\n\tpublic function __destruct()\n\t{\n\t\t$this->free_result();\n\t}\n}\n"
  },
  {
    "path": "system/database/drivers/oci8/oci8_utility.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.4.1\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Oracle Utility Class\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_oci8_utility extends CI_DB_utility {\n\n\t/**\n\t * List databases statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_list_databases\t= 'SELECT username FROM dba_users'; // Schemas are actual usernames\n\n\t/**\n\t * Export\n\t *\n\t * @param\tarray\t$params\tPreferences\n\t * @return\tmixed\n\t */\n\tprotected function _backup($params = array())\n\t{\n\t\t// Currently unsupported\n\t\treturn $this->db->display_error('db_unsupported_feature');\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/odbc/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/database/drivers/odbc/odbc_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.3.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * ODBC Database Adapter Class\n *\n * Note: _DB is an extender class that the app controller\n * creates dynamically based on whether the query builder\n * class is being used or not.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_odbc_driver extends CI_DB_driver {\n\n\t/**\n\t * Database driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $dbdriver = 'odbc';\n\n\t/**\n\t * Database schema\n\t *\n\t * @var\tstring\n\t */\n\tpublic $schema = 'public';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Identifier escape character\n\t *\n\t * Must be empty for ODBC.\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_escape_char = '';\n\n\t/**\n\t * ESCAPE statement string\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_like_escape_str = \" {escape '%s'} \";\n\n\t/**\n\t * ORDER BY random keyword\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_random_keyword = array('RND()', 'RND(%d)');\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ODBC result ID resource returned from odbc_prepare()\n\t *\n\t * @var\tresource\n\t */\n\tprivate $odbc_result;\n\n\t/**\n\t * Values to use with odbc_execute() for prepared statements\n\t *\n\t * @var\tarray\n\t */\n\tprivate $binds = array();\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * @param\tarray\t$params\n\t * @return\tvoid\n\t */\n\tpublic function __construct($params)\n\t{\n\t\tparent::__construct($params);\n\n\t\t// Legacy support for DSN in the hostname field\n\t\tif (empty($this->dsn))\n\t\t{\n\t\t\t$this->dsn = $this->hostname;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Non-persistent database connection\n\t *\n\t * @param\tbool\t$persistent\n\t * @return\tresource\n\t */\n\tpublic function db_connect($persistent = FALSE)\n\t{\n\t\treturn ($persistent === TRUE)\n\t\t\t? odbc_pconnect($this->dsn, $this->username, $this->password)\n\t\t\t: odbc_connect($this->dsn, $this->username, $this->password);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Compile Bindings\n\t *\n\t * @param\tstring\t$sql\tSQL statement\n\t * @param\tarray\t$binds\tAn array of values to bind\n\t * @return\tstring\n\t */\n\tpublic function compile_binds($sql, $binds)\n\t{\n\t\tif (empty($binds) OR empty($this->bind_marker) OR strpos($sql, $this->bind_marker) === FALSE)\n\t\t{\n\t\t\treturn $sql;\n\t\t}\n\t\telseif ( ! is_array($binds))\n\t\t{\n\t\t\t$binds = array($binds);\n\t\t\t$bind_count = 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Make sure we're using numeric keys\n\t\t\t$binds = array_values($binds);\n\t\t\t$bind_count = count($binds);\n\t\t}\n\n\t\t// We'll need the marker length later\n\t\t$ml = strlen($this->bind_marker);\n\n\t\t// Make sure not to replace a chunk inside a string that happens to match the bind marker\n\t\tif ($c = preg_match_all(\"/'[^']*'|\\\"[^\\\"]*\\\"/i\", $sql, $matches))\n\t\t{\n\t\t\t$c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i',\n\t\t\t\tstr_replace($matches[0],\n\t\t\t\t\tstr_replace($this->bind_marker, str_repeat(' ', $ml), $matches[0]),\n\t\t\t\t\t$sql, $c),\n\t\t\t\t$matches, PREG_OFFSET_CAPTURE);\n\n\t\t\t// Bind values' count must match the count of markers in the query\n\t\t\tif ($bind_count !== $c)\n\t\t\t{\n\t\t\t\treturn $sql;\n\t\t\t}\n\t\t}\n\t\telseif (($c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i', $sql, $matches, PREG_OFFSET_CAPTURE)) !== $bind_count)\n\t\t{\n\t\t\treturn $sql;\n\t\t}\n\n\t\tif ($this->bind_marker !== '?')\n\t\t{\n\t\t\tdo\n\t\t\t{\n\t\t\t\t$c--;\n\t\t\t\t$sql = substr_replace($sql, '?', $matches[0][$c][1], $ml);\n\t\t\t}\n\t\t\twhile ($c !== 0);\n\t\t}\n\n\t\tif (FALSE !== ($this->odbc_result = odbc_prepare($this->conn_id, $sql)))\n\t\t{\n\t\t\t$this->binds = array_values($binds);\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Execute the query\n\t *\n\t * @param\tstring\t$sql\tan SQL query\n\t * @return\tresource\n\t */\n\tprotected function _execute($sql)\n\t{\n\t\tif ( ! isset($this->odbc_result))\n\t\t{\n\t\t\treturn odbc_exec($this->conn_id, $sql);\n\t\t}\n\t\telseif ($this->odbc_result === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif (TRUE === ($success = odbc_execute($this->odbc_result, $this->binds)))\n\t\t{\n\t\t\t// For queries that return result sets, return the result_id resource on success\n\t\t\t$this->is_write_type($sql) OR $success = $this->odbc_result;\n\t\t}\n\n\t\t$this->odbc_result = NULL;\n\t\t$this->binds       = array();\n\n\t\treturn $success;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Begin Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_begin()\n\t{\n\t\treturn odbc_autocommit($this->conn_id, FALSE);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Commit Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_commit()\n\t{\n\t\tif (odbc_commit($this->conn_id))\n\t\t{\n\t\t\todbc_autocommit($this->conn_id, TRUE);\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Rollback Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_rollback()\n\t{\n\t\tif (odbc_rollback($this->conn_id))\n\t\t{\n\t\t\todbc_autocommit($this->conn_id, TRUE);\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Determines if a query is a \"write\" type.\n\t *\n\t * @param\tstring\tAn SQL query string\n\t * @return\tbool\n\t */\n\tpublic function is_write_type($sql)\n\t{\n\t\tif (preg_match('#^(INSERT|UPDATE).*RETURNING\\s.+(\\,\\s?.+)*$#is', $sql))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn parent::is_write_type($sql);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Platform-dependent string escape\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tprotected function _escape_str($str)\n\t{\n\t\t$this->display_error('db_unsupported_feature');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Affected Rows\n\t *\n\t * @return\tint\n\t */\n\tpublic function affected_rows()\n\t{\n\t\treturn odbc_num_rows($this->result_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert ID\n\t *\n\t * @return\tbool\n\t */\n\tpublic function insert_id()\n\t{\n\t\treturn ($this->db_debug) ? $this->display_error('db_unsupported_feature') : FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show table query\n\t *\n\t * Generates a platform-specific query string so that the table names can be fetched\n\t *\n\t * @param\tbool\t$prefix_limit\n\t * @return\tstring\n\t */\n\tprotected function _list_tables($prefix_limit = FALSE)\n\t{\n\t\t$sql = \"SELECT table_name FROM information_schema.tables WHERE table_schema = '\".$this->schema.\"'\";\n\n\t\tif ($prefix_limit !== FALSE && $this->dbprefix !== '')\n\t\t{\n\t\t\treturn $sql.\" AND table_name LIKE '\".$this->escape_like_str($this->dbprefix).\"%' \"\n\t\t\t\t.sprintf($this->_like_escape_str, $this->_like_escape_chr);\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show column query\n\t *\n\t * Generates a platform-specific query string so that the column names can be fetched\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _list_columns($table = '')\n\t{\n\t\treturn 'SHOW COLUMNS FROM '.$table;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field data query\n\t *\n\t * Generates a platform-specific query so that the column data can be retrieved\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _field_data($table)\n\t{\n\t\treturn 'SELECT TOP 1 FROM '.$table;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Error\n\t *\n\t * Returns an array containing code and message of the last\n\t * database error that has occurred.\n\t *\n\t * @return\tarray\n\t */\n\tpublic function error()\n\t{\n\t\treturn array('code' => odbc_error($this->conn_id), 'message' => odbc_errormsg($this->conn_id));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Close DB Connection\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _close()\n\t{\n\t\todbc_close($this->conn_id);\n\t}\n}\n"
  },
  {
    "path": "system/database/drivers/odbc/odbc_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.3.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * ODBC Forge Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/database/\n */\nclass CI_DB_odbc_forge extends CI_DB_forge {\n\n\t/**\n\t * CREATE TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_table_if\t= FALSE;\n\n\t/**\n\t * DROP TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_drop_table_if\t= FALSE;\n\n\t/**\n\t * UNSIGNED support\n\t *\n\t * @var\tbool|array\n\t */\n\tprotected $_unsigned\t\t= FALSE;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute AUTO_INCREMENT\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_auto_increment(&$attributes, &$field)\n\t{\n\t\t// Not supported (in most databases at least)\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/odbc/odbc_result.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.3.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * ODBC Result Class\n *\n * This class extends the parent result class: CI_DB_result\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_odbc_result extends CI_DB_result {\n\n\t/**\n\t * Number of rows in the result set\n\t *\n\t * @return\tint\n\t */\n\tpublic function num_rows()\n\t{\n\t\tif (is_int($this->num_rows))\n\t\t{\n\t\t\treturn $this->num_rows;\n\t\t}\n\t\telseif (($this->num_rows = odbc_num_rows($this->result_id)) !== -1)\n\t\t{\n\t\t\treturn $this->num_rows;\n\t\t}\n\n\t\t// Work-around for ODBC subdrivers that don't support num_rows()\n\t\tif (count($this->result_array) > 0)\n\t\t{\n\t\t\treturn $this->num_rows = count($this->result_array);\n\t\t}\n\t\telseif (count($this->result_object) > 0)\n\t\t{\n\t\t\treturn $this->num_rows = count($this->result_object);\n\t\t}\n\n\t\treturn $this->num_rows = count($this->result_array());\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Number of fields in the result set\n\t *\n\t * @return\tint\n\t */\n\tpublic function num_fields()\n\t{\n\t\treturn odbc_num_fields($this->result_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch Field Names\n\t *\n\t * Generates an array of column names\n\t *\n\t * @return\tarray\n\t */\n\tpublic function list_fields()\n\t{\n\t\t$field_names = array();\n\t\t$num_fields = $this->num_fields();\n\n\t\tif ($num_fields > 0)\n\t\t{\n\t\t\tfor ($i = 1; $i <= $num_fields; $i++)\n\t\t\t{\n\t\t\t\t$field_names[] = odbc_field_name($this->result_id, $i);\n\t\t\t}\n\t\t}\n\n\t\treturn $field_names;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field data\n\t *\n\t * Generates an array of objects containing field meta-data\n\t *\n\t * @return\tarray\n\t */\n\tpublic function field_data()\n\t{\n\t\t$retval = array();\n\t\tfor ($i = 0, $odbc_index = 1, $c = $this->num_fields(); $i < $c; $i++, $odbc_index++)\n\t\t{\n\t\t\t$retval[$i]\t\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t\t= odbc_field_name($this->result_id, $odbc_index);\n\t\t\t$retval[$i]->type\t\t= odbc_field_type($this->result_id, $odbc_index);\n\t\t\t$retval[$i]->max_length\t\t= odbc_field_len($this->result_id, $odbc_index);\n\t\t\t$retval[$i]->primary_key\t= 0;\n\t\t\t$retval[$i]->default\t\t= '';\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Free the result\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function free_result()\n\t{\n\t\tif (is_resource($this->result_id))\n\t\t{\n\t\t\todbc_free_result($this->result_id);\n\t\t\t$this->result_id = FALSE;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - associative array\n\t *\n\t * Returns the result set as an array\n\t *\n\t * @return\tarray\n\t */\n\tprotected function _fetch_assoc()\n\t{\n\t\treturn odbc_fetch_array($this->result_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - object\n\t *\n\t * Returns the result set as an object\n\t *\n\t * @param\tstring\t$class_name\n\t * @return\tobject\n\t */\n\tprotected function _fetch_object($class_name = 'stdClass')\n\t{\n\t\t$row = odbc_fetch_object($this->result_id);\n\n\t\tif ($class_name === 'stdClass' OR ! $row)\n\t\t{\n\t\t\treturn $row;\n\t\t}\n\n\t\t$class_name = new $class_name();\n\t\tforeach ($row as $key => $value)\n\t\t{\n\t\t\t$class_name->$key = $value;\n\t\t}\n\n\t\treturn $class_name;\n\t}\n\n}\n\n// --------------------------------------------------------------------\n\nif ( ! function_exists('odbc_fetch_array'))\n{\n\t/**\n\t * ODBC Fetch array\n\t *\n\t * Emulates the native odbc_fetch_array() function when\n\t * it is not available (odbc_fetch_array() requires unixODBC)\n\t *\n\t * @param\tresource\t&$result\n\t * @param\tint\t\t$rownumber\n\t * @return\tarray\n\t */\n\tfunction odbc_fetch_array(&$result, $rownumber = 1)\n\t{\n\t\t$rs = array();\n\t\tif ( ! odbc_fetch_into($result, $rs, $rownumber))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$rs_assoc = array();\n\t\tforeach ($rs as $k => $v)\n\t\t{\n\t\t\t$field_name = odbc_field_name($result, $k+1);\n\t\t\t$rs_assoc[$field_name] = $v;\n\t\t}\n\n\t\treturn $rs_assoc;\n\t}\n}\n\n// --------------------------------------------------------------------\n\nif ( ! function_exists('odbc_fetch_object'))\n{\n\t/**\n\t * ODBC Fetch object\n\t *\n\t * Emulates the native odbc_fetch_object() function when\n\t * it is not available.\n\t *\n\t * @param\tresource\t&$result\n\t * @param\tint\t\t$rownumber\n\t * @return\tobject\n\t */\n\tfunction odbc_fetch_object(&$result, $rownumber = 1)\n\t{\n\t\t$rs = array();\n\t\tif ( ! odbc_fetch_into($result, $rs, $rownumber))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$rs_object = new stdClass();\n\t\tforeach ($rs as $k => $v)\n\t\t{\n\t\t\t$field_name = odbc_field_name($result, $k+1);\n\t\t\t$rs_object->$field_name = $v;\n\t\t}\n\n\t\treturn $rs_object;\n\t}\n}\n"
  },
  {
    "path": "system/database/drivers/odbc/odbc_utility.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.3.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * ODBC Utility Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/database/\n */\nclass CI_DB_odbc_utility extends CI_DB_utility {\n\n\t/**\n\t * Export\n\t *\n\t * @param\tarray\t$params\tPreferences\n\t * @return\tmixed\n\t */\n\tprotected function _backup($params = array())\n\t{\n\t\t// Currently unsupported\n\t\treturn $this->db->display_error('db_unsupported_feature');\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/database/drivers/pdo/pdo_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 2.1.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO Database Adapter Class\n *\n * Note: _DB is an extender class that the app controller\n * creates dynamically based on whether the query builder\n * class is being used or not.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_driver extends CI_DB {\n\n\t/**\n\t * Database driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $dbdriver = 'pdo';\n\n\t/**\n\t * PDO Options\n\t *\n\t * @var\tarray\n\t */\n\tpublic $options = array();\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * Validates the DSN string and/or detects the subdriver.\n\t *\n\t * @param\tarray\t$params\n\t * @return\tvoid\n\t */\n\tpublic function __construct($params)\n\t{\n\t\tparent::__construct($params);\n\n\t\tif (preg_match('/([^:]+):/', $this->dsn, $match) && count($match) === 2)\n\t\t{\n\t\t\t// If there is a minimum valid dsn string pattern found, we're done\n\t\t\t// This is for general PDO users, who tend to have a full DSN string.\n\t\t\t$this->subdriver = $match[1];\n\t\t\treturn;\n\t\t}\n\t\t// Legacy support for DSN specified in the hostname field\n\t\telseif (preg_match('/([^:]+):/', $this->hostname, $match) && count($match) === 2)\n\t\t{\n\t\t\t$this->dsn = $this->hostname;\n\t\t\t$this->hostname = NULL;\n\t\t\t$this->subdriver = $match[1];\n\t\t\treturn;\n\t\t}\n\t\telseif (in_array($this->subdriver, array('mssql', 'sybase'), TRUE))\n\t\t{\n\t\t\t$this->subdriver = 'dblib';\n\t\t}\n\t\telseif ($this->subdriver === '4D')\n\t\t{\n\t\t\t$this->subdriver = '4d';\n\t\t}\n\t\telseif ( ! in_array($this->subdriver, array('4d', 'cubrid', 'dblib', 'firebird', 'ibm', 'informix', 'mysql', 'oci', 'odbc', 'pgsql', 'sqlite', 'sqlsrv'), TRUE))\n\t\t{\n\t\t\tlog_message('error', 'PDO: Invalid or non-existent subdriver');\n\n\t\t\tif ($this->db_debug)\n\t\t\t{\n\t\t\t\tshow_error('Invalid or non-existent PDO subdriver');\n\t\t\t}\n\t\t}\n\n\t\t$this->dsn = NULL;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Database connection\n\t *\n\t * @param\tbool\t$persistent\n\t * @return\tobject\n\t */\n\tpublic function db_connect($persistent = FALSE)\n\t{\n\t\tif ($persistent === TRUE)\n\t\t{\n\t\t\t$this->options[PDO::ATTR_PERSISTENT] = TRUE;\n\t\t}\n\n\t\t// From PHP8.0, default PDO::ATTR_ERRMODE is changed\n\t\t// from PDO::ERRMODE_SILENT to PDO::ERRMODE_EXCEPTION\n\t\t// as https://wiki.php.net/rfc/pdo_default_errmode\n\t\tif ( ! isset($this->options[PDO::ATTR_ERRMODE]))\n\t\t{\n\t\t\t$this->options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_SILENT;\n\t\t}\n\n\t\ttry\n\t\t{\n\t\t\treturn new PDO($this->dsn, $this->username, $this->password, $this->options);\n\t\t}\n\t\tcatch (PDOException $e)\n\t\t{\n\t\t\tif ($this->db_debug && empty($this->failover))\n\t\t\t{\n\t\t\t\t$this->display_error($e->getMessage(), '', TRUE);\n\t\t\t}\n\n\t\t\treturn FALSE;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Database version number\n\t *\n\t * @return\tstring\n\t */\n\tpublic function version()\n\t{\n\t\tif (isset($this->data_cache['version']))\n\t\t{\n\t\t\treturn $this->data_cache['version'];\n\t\t}\n\n\t\t// Not all subdrivers support the getAttribute() method\n\t\ttry\n\t\t{\n\t\t\treturn $this->data_cache['version'] = $this->conn_id->getAttribute(PDO::ATTR_SERVER_VERSION);\n\t\t}\n\t\tcatch (PDOException $e)\n\t\t{\n\t\t\treturn parent::version();\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Execute the query\n\t *\n\t * @param\tstring\t$sql\tSQL query\n\t * @return\tmixed\n\t */\n\tprotected function _execute($sql)\n\t{\n\t\treturn $this->conn_id->query($sql);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Begin Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_begin()\n\t{\n\t\treturn $this->conn_id->beginTransaction();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Commit Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_commit()\n\t{\n\t\treturn $this->conn_id->commit();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Rollback Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_rollback()\n\t{\n\t\treturn $this->conn_id->rollBack();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Platform-dependent string escape\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tprotected function _escape_str($str)\n\t{\n\t\t// Escape the string\n\t\t$str = $this->conn_id->quote($str);\n\n\t\t// If there are duplicated quotes, trim them away\n\t\treturn ($str[0] === \"'\")\n\t\t\t? substr($str, 1, -1)\n\t\t\t: $str;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Affected Rows\n\t *\n\t * @return\tint\n\t */\n\tpublic function affected_rows()\n\t{\n\t\treturn is_object($this->result_id) ? $this->result_id->rowCount() : 0;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert ID\n\t *\n\t * @param\tstring\t$name\n\t * @return\tint\n\t */\n\tpublic function insert_id($name = NULL)\n\t{\n\t\treturn $this->conn_id->lastInsertId($name);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field data query\n\t *\n\t * Generates a platform-specific query so that the column data can be retrieved\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _field_data($table)\n\t{\n\t\treturn 'SELECT TOP 1 * FROM '.$this->protect_identifiers($table);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Error\n\t *\n\t * Returns an array containing code and message of the last\n\t * database error that has occurred.\n\t *\n\t * @return\tarray\n\t */\n\tpublic function error()\n\t{\n\t\t$error = array('code' => '00000', 'message' => '');\n\t\t$pdo_error = $this->conn_id->errorInfo();\n\n\t\tif (empty($pdo_error[0]))\n\t\t{\n\t\t\treturn $error;\n\t\t}\n\n\t\t$error['code'] = isset($pdo_error[1]) ? $pdo_error[0].'/'.$pdo_error[1] : $pdo_error[0];\n\t\tif (isset($pdo_error[2]))\n\t\t{\n\t\t\t$error['message'] = $pdo_error[2];\n\t\t}\n\n\t\treturn $error;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Truncate statement\n\t *\n\t * Generates a platform-specific truncate string from the supplied data\n\t *\n\t * If the database does not support the TRUNCATE statement,\n\t * then this method maps to 'DELETE FROM table'\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _truncate($table)\n\t{\n\t\treturn 'TRUNCATE TABLE '.$table;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Close DB Connection\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _close()\n\t{\n\t\t$this->result_id = FALSE;\n\t\t$this->conn_id = FALSE;\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/pdo_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 2.1.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO Forge Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/database/\n */\nclass CI_DB_pdo_forge extends CI_DB_forge {\n\n\t/**\n\t * CREATE TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_table_if\t= FALSE;\n\n\t/**\n\t * DROP TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_drop_table_if\t= FALSE;\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/pdo_result.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 2.1.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO Result Class\n *\n * This class extends the parent result class: CI_DB_result\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_result extends CI_DB_result {\n\n\t/**\n\t * Number of rows in the result set\n\t *\n\t * @return\tint\n\t */\n\tpublic function num_rows()\n\t{\n\t\tif (is_int($this->num_rows))\n\t\t{\n\t\t\treturn $this->num_rows;\n\t\t}\n\t\telseif (count($this->result_array) > 0)\n\t\t{\n\t\t\treturn $this->num_rows = count($this->result_array);\n\t\t}\n\t\telseif (count($this->result_object) > 0)\n\t\t{\n\t\t\treturn $this->num_rows = count($this->result_object);\n\t\t}\n\t\telseif (($num_rows = $this->result_id->rowCount()) > 0)\n\t\t{\n\t\t\treturn $this->num_rows = $num_rows;\n\t\t}\n\n\t\treturn $this->num_rows = count($this->result_array());\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Number of fields in the result set\n\t *\n\t * @return\tint\n\t */\n\tpublic function num_fields()\n\t{\n\t\treturn $this->result_id->columnCount();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch Field Names\n\t *\n\t * Generates an array of column names\n\t *\n\t * @return\tbool\n\t */\n\tpublic function list_fields()\n\t{\n\t\t$field_names = array();\n\t\tfor ($i = 0, $c = $this->num_fields(); $i < $c; $i++)\n\t\t{\n\t\t\t// Might trigger an E_WARNING due to not all subdrivers\n\t\t\t// supporting getColumnMeta()\n\t\t\t$field_names[$i] = @$this->result_id->getColumnMeta($i);\n\t\t\t$field_names[$i] = $field_names[$i]['name'];\n\t\t}\n\n\t\treturn $field_names;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field data\n\t *\n\t * Generates an array of objects containing field meta-data\n\t *\n\t * @return\tarray\n\t */\n\tpublic function field_data()\n\t{\n\t\ttry\n\t\t{\n\t\t\t$retval = array();\n\n\t\t\tfor ($i = 0, $c = $this->num_fields(); $i < $c; $i++)\n\t\t\t{\n\t\t\t\t$field = $this->result_id->getColumnMeta($i);\n\n\t\t\t\t$retval[$i]\t\t\t= new stdClass();\n\t\t\t\t$retval[$i]->name\t\t= $field['name'];\n\t\t\t\t$retval[$i]->type\t\t= isset($field['native_type']) ? $field['native_type'] : null;\n\t\t\t\t$retval[$i]->max_length\t\t= ($field['len'] > 0) ? $field['len'] : NULL;\n\t\t\t\t$retval[$i]->primary_key\t= (int) ( ! empty($field['flags']) && in_array('primary_key', $field['flags'], TRUE));\n\t\t\t}\n\n\t\t\treturn $retval;\n\t\t}\n\t\tcatch (Exception $e)\n\t\t{\n\t\t\tif ($this->db->db_debug)\n\t\t\t{\n\t\t\t\treturn $this->db->display_error('db_unsupported_feature');\n\t\t\t}\n\n\t\t\treturn FALSE;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Free the result\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function free_result()\n\t{\n\t\tif (is_object($this->result_id))\n\t\t{\n\t\t\t$this->result_id = FALSE;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - associative array\n\t *\n\t * Returns the result set as an array\n\t *\n\t * @return\tarray\n\t */\n\tprotected function _fetch_assoc()\n\t{\n\t\treturn $this->result_id->fetch(PDO::FETCH_ASSOC);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - object\n\t *\n\t * Returns the result set as an object\n\t *\n\t * @param\tstring\t$class_name\n\t * @return\tobject\n\t */\n\tprotected function _fetch_object($class_name = 'stdClass')\n\t{\n\t\treturn $this->result_id->fetchObject($class_name);\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/pdo_utility.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 2.1.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO Utility Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/database/\n */\nclass CI_DB_pdo_utility extends CI_DB_utility {\n\n\t/**\n\t * Export\n\t *\n\t * @param\tarray\t$params\tPreferences\n\t * @return\tmixed\n\t */\n\tprotected function _backup($params = array())\n\t{\n\t\t// Currently unsupported\n\t\treturn $this->db->display_error('db_unsupported_feature');\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_4d_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO 4D Database Adapter Class\n *\n * Note: _DB is an extender class that the app controller\n * creates dynamically based on whether the query builder\n * class is being used or not.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_4d_driver extends CI_DB_pdo_driver {\n\n\t/**\n\t * Sub-driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $subdriver = '4d';\n\n\t/**\n\t * Identifier escape character\n\t *\n\t * @var\tstring[]\n\t */\n\tprotected $_escape_char = array('[', ']');\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * Builds the DSN if not already set.\n\t *\n\t * @param\tarray\t$params\n\t * @return\tvoid\n\t */\n\tpublic function __construct($params)\n\t{\n\t\tparent::__construct($params);\n\n\t\tif (empty($this->dsn))\n\t\t{\n\t\t\t$this->dsn = '4D:host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);\n\n\t\t\tempty($this->port) OR $this->dsn .= ';port='.$this->port;\n\t\t\tempty($this->database) OR $this->dsn .= ';dbname='.$this->database;\n\t\t\tempty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;\n\t\t}\n\t\telseif ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 3) === FALSE)\n\t\t{\n\t\t\t$this->dsn .= ';charset='.$this->char_set;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show table query\n\t *\n\t * Generates a platform-specific query string so that the table names can be fetched\n\t *\n\t * @param\tbool\t$prefix_limit\n\t * @return\tstring\n\t */\n\tprotected function _list_tables($prefix_limit = FALSE)\n\t{\n\t\t$sql = 'SELECT '.$this->escape_identifiers('TABLE_NAME').' FROM '.$this->escape_identifiers('_USER_TABLES');\n\n\t\tif ($prefix_limit === TRUE && $this->dbprefix !== '')\n\t\t{\n\t\t\t$sql .= ' WHERE '.$this->escape_identifiers('TABLE_NAME').\" LIKE '\".$this->escape_like_str($this->dbprefix).\"%' \"\n\t\t\t\t.sprintf($this->_like_escape_str, $this->_like_escape_chr);\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show column query\n\t *\n\t * Generates a platform-specific query string so that the column names can be fetched\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _list_columns($table = '')\n\t{\n\t\treturn 'SELECT '.$this->escape_identifiers('COLUMN_NAME').' FROM '.$this->escape_identifiers('_USER_COLUMNS')\n\t\t\t.' WHERE '.$this->escape_identifiers('TABLE_NAME').' = '.$this->escape($table);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field data query\n\t *\n\t * Generates a platform-specific query so that the column data can be retrieved\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _field_data($table)\n\t{\n\t\treturn 'SELECT * FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE).' LIMIT 1';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Update statement\n\t *\n\t * Generates a platform-specific update string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @param\tarray\t$values\n\t * @return\tstring\n\t */\n\tprotected function _update($table, $values)\n\t{\n\t\t$this->qb_limit = FALSE;\n\t\t$this->qb_orderby = array();\n\t\treturn parent::_update($table, $values);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Delete statement\n\t *\n\t * Generates a platform-specific delete string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _delete($table)\n\t{\n\t\t$this->qb_limit = FALSE;\n\t\treturn parent::_delete($table);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * LIMIT\n\t *\n\t * Generates a platform-specific LIMIT clause\n\t *\n\t * @param\tstring\t$sql\tSQL Query\n\t * @return\tstring\n\t */\n\tprotected function _limit($sql)\n\t{\n\t\treturn $sql.' LIMIT '.$this->qb_limit.($this->qb_offset ? ' OFFSET '.$this->qb_offset : '');\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_4d_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO 4D Forge Class\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_4d_forge extends CI_DB_pdo_forge {\n\n\t/**\n\t * CREATE DATABASE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_database\t= 'CREATE SCHEMA %s';\n\n\t/**\n\t * DROP DATABASE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_drop_database\t= 'DROP SCHEMA %s';\n\n\t/**\n\t * CREATE TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_table_if\t= 'CREATE TABLE IF NOT EXISTS';\n\n\t/**\n\t * RENAME TABLE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_rename_table\t= FALSE;\n\n\t/**\n\t * DROP TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_drop_table_if\t= 'DROP TABLE IF EXISTS';\n\n\t/**\n\t * UNSIGNED support\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_unsigned\t\t= array(\n\t\t'INT16'\t\t=> 'INT',\n\t\t'SMALLINT'\t=> 'INT',\n\t\t'INT'\t\t=> 'INT64',\n\t\t'INT32'\t\t=> 'INT64'\n\t);\n\n\t/**\n\t * DEFAULT value representation in CREATE/ALTER TABLE statements\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_default\t\t= FALSE;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ALTER TABLE\n\t *\n\t * @param\tstring\t$alter_type\tALTER type\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tmixed\t$field\t\tColumn definition\n\t * @return\tstring|string[]\n\t */\n\tprotected function _alter_table($alter_type, $table, $field)\n\t{\n\t\tif (in_array($alter_type, array('ADD', 'DROP'), TRUE))\n\t\t{\n\t\t\treturn parent::_alter_table($alter_type, $table, $field);\n\t\t}\n\n\t\t// No method of modifying columns is supported\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Process column\n\t *\n\t * @param\tarray\t$field\n\t * @return\tstring\n\t */\n\tprotected function _process_column($field)\n\t{\n\t\treturn $this->db->escape_identifiers($field['name'])\n\t\t\t.' '.$field['type'].$field['length']\n\t\t\t.$field['null']\n\t\t\t.$field['unique']\n\t\t\t.$field['auto_increment'];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute TYPE\n\t *\n\t * Performs a data type mapping between different databases.\n\t *\n\t * @param\tarray\t&$attributes\n\t * @return\tvoid\n\t */\n\tprotected function _attr_type(&$attributes)\n\t{\n\t\tswitch (strtoupper($attributes['TYPE']))\n\t\t{\n\t\t\tcase 'TINYINT':\n\t\t\t\t$attributes['TYPE'] = 'SMALLINT';\n\t\t\t\t$attributes['UNSIGNED'] = FALSE;\n\t\t\t\treturn;\n\t\t\tcase 'MEDIUMINT':\n\t\t\t\t$attributes['TYPE'] = 'INTEGER';\n\t\t\t\t$attributes['UNSIGNED'] = FALSE;\n\t\t\t\treturn;\n\t\t\tcase 'INTEGER':\n\t\t\t\t$attributes['TYPE'] = 'INT';\n\t\t\t\treturn;\n\t\t\tcase 'BIGINT':\n\t\t\t\t$attributes['TYPE'] = 'INT64';\n\t\t\t\treturn;\n\t\t\tdefault: return;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute UNIQUE\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_unique(&$attributes, &$field)\n\t{\n\t\tif ( ! empty($attributes['UNIQUE']) && $attributes['UNIQUE'] === TRUE)\n\t\t{\n\t\t\t$field['unique'] = ' UNIQUE';\n\n\t\t\t// UNIQUE must be used with NOT NULL\n\t\t\t$field['null'] = ' NOT NULL';\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute AUTO_INCREMENT\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_auto_increment(&$attributes, &$field)\n\t{\n\t\tif ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE)\n\t\t{\n\t\t\tif (stripos($field['type'], 'int') !== FALSE)\n\t\t\t{\n\t\t\t\t$field['auto_increment'] = ' AUTO_INCREMENT';\n\t\t\t}\n\t\t\telseif (strcasecmp($field['type'], 'UUID') === 0)\n\t\t\t{\n\t\t\t\t$field['auto_increment'] = ' AUTO_GENERATE';\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO CUBRID Database Adapter Class\n *\n * Note: _DB is an extender class that the app controller\n * creates dynamically based on whether the query builder\n * class is being used or not.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_cubrid_driver extends CI_DB_pdo_driver {\n\n\t/**\n\t * Sub-driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $subdriver = 'cubrid';\n\n\t/**\n\t * Identifier escape character\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_escape_char = '`';\n\n\t/**\n\t * ORDER BY random keyword\n\t *\n\t * @var array\n\t */\n\tprotected $_random_keyword = array('RANDOM()', 'RANDOM(%d)');\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * Builds the DSN if not already set.\n\t *\n\t * @param\tarray\t$params\n\t * @return\tvoid\n\t */\n\tpublic function __construct($params)\n\t{\n\t\tparent::__construct($params);\n\n\t\tif (empty($this->dsn))\n\t\t{\n\t\t\t$this->dsn = 'cubrid:host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);\n\n\t\t\tempty($this->port) OR $this->dsn .= ';port='.$this->port;\n\t\t\tempty($this->database) OR $this->dsn .= ';dbname='.$this->database;\n\t\t\tempty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show table query\n\t *\n\t * Generates a platform-specific query string so that the table names can be fetched\n\t *\n\t * @param\tbool\t$prefix_limit\n\t * @return\tstring\n\t */\n\tprotected function _list_tables($prefix_limit = FALSE)\n\t{\n\t\t$sql = 'SHOW TABLES';\n\n\t\tif ($prefix_limit === TRUE && $this->dbprefix !== '')\n\t\t{\n\t\t\treturn $sql.\" LIKE '\".$this->escape_like_str($this->dbprefix).\"%'\";\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show column query\n\t *\n\t * Generates a platform-specific query string so that the column names can be fetched\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _list_columns($table = '')\n\t{\n\t\treturn 'SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns an object with field data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tarray\n\t */\n\tpublic function field_data($table)\n\t{\n\t\tif (($query = $this->query('SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE))) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\t\t$query = $query->result_object();\n\n\t\t$retval = array();\n\t\tfor ($i = 0, $c = count($query); $i < $c; $i++)\n\t\t{\n\t\t\t$retval[$i]\t\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t\t= $query[$i]->Field;\n\n\t\t\tsscanf($query[$i]->Type, '%[a-z](%d)',\n\t\t\t\t$retval[$i]->type,\n\t\t\t\t$retval[$i]->max_length\n\t\t\t);\n\n\t\t\t$retval[$i]->default\t\t= $query[$i]->Default;\n\t\t\t$retval[$i]->primary_key\t= (int) ($query[$i]->Key === 'PRI');\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Truncate statement\n\t *\n\t * Generates a platform-specific truncate string from the supplied data\n\t *\n\t * If the database does not support the TRUNCATE statement,\n\t * then this method maps to 'DELETE FROM table'\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _truncate($table)\n\t{\n\t\treturn 'TRUNCATE '.$table;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * FROM tables\n\t *\n\t * Groups tables in FROM clauses if needed, so there is no confusion\n\t * about operator precedence.\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _from_tables()\n\t{\n\t\tif ( ! empty($this->qb_join) && count($this->qb_from) > 1)\n\t\t{\n\t\t\treturn '('.implode(', ', $this->qb_from).')';\n\t\t}\n\n\t\treturn implode(', ', $this->qb_from);\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_cubrid_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO CUBRID Forge Class\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_cubrid_forge extends CI_DB_pdo_forge {\n\n\t/**\n\t * CREATE DATABASE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_database\t= FALSE;\n\n\t/**\n\t * DROP DATABASE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_drop_database\t= FALSE;\n\n\t/**\n\t * CREATE TABLE keys flag\n\t *\n\t * Whether table keys are created from within the\n\t * CREATE TABLE statement.\n\t *\n\t * @var\tbool\n\t */\n\tprotected $_create_table_keys\t= TRUE;\n\n\t/**\n\t * DROP TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_drop_table_if\t= 'DROP TABLE IF EXISTS';\n\n\t/**\n\t * UNSIGNED support\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_unsigned\t\t= array(\n\t\t'SHORT'\t\t=> 'INTEGER',\n\t\t'SMALLINT'\t=> 'INTEGER',\n\t\t'INT'\t\t=> 'BIGINT',\n\t\t'INTEGER'\t=> 'BIGINT',\n\t\t'BIGINT'\t=> 'NUMERIC',\n\t\t'FLOAT'\t\t=> 'DOUBLE',\n\t\t'REAL'\t\t=> 'DOUBLE'\n\t);\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ALTER TABLE\n\t *\n\t * @param\tstring\t$alter_type\tALTER type\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tmixed\t$field\t\tColumn definition\n\t * @return\tstring|string[]\n\t */\n\tprotected function _alter_table($alter_type, $table, $field)\n\t{\n\t\tif (in_array($alter_type, array('DROP', 'ADD'), TRUE))\n\t\t{\n\t\t\treturn parent::_alter_table($alter_type, $table, $field);\n\t\t}\n\n\t\t$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table);\n\t\t$sqls = array();\n\t\tfor ($i = 0, $c = count($field); $i < $c; $i++)\n\t\t{\n\t\t\tif ($field[$i]['_literal'] !== FALSE)\n\t\t\t{\n\t\t\t\t$sqls[] = $sql.' CHANGE '.$field[$i]['_literal'];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$alter_type = empty($field[$i]['new_name']) ? ' MODIFY ' : ' CHANGE ';\n\t\t\t\t$sqls[] = $sql.$alter_type.$this->_process_column($field[$i]);\n\t\t\t}\n\t\t}\n\n\t\treturn $sqls;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Process column\n\t *\n\t * @param\tarray\t$field\n\t * @return\tstring\n\t */\n\tprotected function _process_column($field)\n\t{\n\t\t$extra_clause = isset($field['after'])\n\t\t\t? ' AFTER '.$this->db->escape_identifiers($field['after']) : '';\n\n\t\tif (empty($extra_clause) && isset($field['first']) && $field['first'] === TRUE)\n\t\t{\n\t\t\t$extra_clause = ' FIRST';\n\t\t}\n\n\t\treturn $this->db->escape_identifiers($field['name'])\n\t\t\t.(empty($field['new_name']) ? '' : ' '.$this->db->escape_identifiers($field['new_name']))\n\t\t\t.' '.$field['type'].$field['length']\n\t\t\t.$field['unsigned']\n\t\t\t.$field['null']\n\t\t\t.$field['default']\n\t\t\t.$field['auto_increment']\n\t\t\t.$field['unique']\n\t\t\t.$extra_clause;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute TYPE\n\t *\n\t * Performs a data type mapping between different databases.\n\t *\n\t * @param\tarray\t&$attributes\n\t * @return\tvoid\n\t */\n\tprotected function _attr_type(&$attributes)\n\t{\n\t\tswitch (strtoupper($attributes['TYPE']))\n\t\t{\n\t\t\tcase 'TINYINT':\n\t\t\t\t$attributes['TYPE'] = 'SMALLINT';\n\t\t\t\t$attributes['UNSIGNED'] = FALSE;\n\t\t\t\treturn;\n\t\t\tcase 'MEDIUMINT':\n\t\t\t\t$attributes['TYPE'] = 'INTEGER';\n\t\t\t\t$attributes['UNSIGNED'] = FALSE;\n\t\t\t\treturn;\n\t\t\tcase 'LONGTEXT':\n\t\t\t\t$attributes['TYPE'] = 'STRING';\n\t\t\t\treturn;\n\t\t\tdefault: return;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Process indexes\n\t *\n\t * @param\tstring\t$table\t(ignored)\n\t * @return\tstring\n\t */\n\tprotected function _process_indexes($table)\n\t{\n\t\t$sql = '';\n\n\t\tfor ($i = 0, $c = count($this->keys); $i < $c; $i++)\n\t\t{\n\t\t\tif (is_array($this->keys[$i]))\n\t\t\t{\n\t\t\t\tfor ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++)\n\t\t\t\t{\n\t\t\t\t\tif ( ! isset($this->fields[$this->keys[$i][$i2]]))\n\t\t\t\t\t{\n\t\t\t\t\t\tunset($this->keys[$i][$i2]);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telseif ( ! isset($this->fields[$this->keys[$i]]))\n\t\t\t{\n\t\t\t\tunset($this->keys[$i]);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tis_array($this->keys[$i]) OR $this->keys[$i] = array($this->keys[$i]);\n\n\t\t\t$sql .= \",\\n\\tKEY \".$this->db->escape_identifiers(implode('_', $this->keys[$i]))\n\t\t\t\t.' ('.implode(', ', $this->db->escape_identifiers($this->keys[$i])).')';\n\t\t}\n\n\t\t$this->keys = array();\n\n\t\treturn $sql;\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO DBLIB Database Adapter Class\n *\n * Note: _DB is an extender class that the app controller\n * creates dynamically based on whether the query builder\n * class is being used or not.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_dblib_driver extends CI_DB_pdo_driver {\n\n\t/**\n\t * Sub-driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $subdriver = 'dblib';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ORDER BY random keyword\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_random_keyword = array('NEWID()', 'RAND(%d)');\n\n\t/**\n\t * Quoted identifier flag\n\t *\n\t * Whether to use SQL-92 standard quoted identifier\n\t * (double quotes) or brackets for identifier escaping.\n\t *\n\t * @var\tbool\n\t */\n\tprotected $_quoted_identifier;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * Builds the DSN if not already set.\n\t *\n\t * @param\tarray\t$params\n\t * @return\tvoid\n\t */\n\tpublic function __construct($params)\n\t{\n\t\tparent::__construct($params);\n\n\t\tif (empty($this->dsn))\n\t\t{\n\t\t\t$this->dsn = $params['subdriver'].':host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);\n\n\t\t\tif ( ! empty($this->port))\n\t\t\t{\n\t\t\t\t$this->dsn .= (DIRECTORY_SEPARATOR === '\\\\' ? ',' : ':').$this->port;\n\t\t\t}\n\n\t\t\tempty($this->database) OR $this->dsn .= ';dbname='.$this->database;\n\t\t\tempty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;\n\t\t\tempty($this->appname) OR $this->dsn .= ';appname='.$this->appname;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 6) === FALSE)\n\t\t\t{\n\t\t\t\t$this->dsn .= ';charset='.$this->char_set;\n\t\t\t}\n\n\t\t\t$this->subdriver = 'dblib';\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Database connection\n\t *\n\t * @param\tbool\t$persistent\n\t * @return\tobject\n\t */\n\tpublic function db_connect($persistent = FALSE)\n\t{\n\t\tif ($persistent === TRUE)\n\t\t{\n\t\t\tlog_message('debug', \"dblib driver doesn't support persistent connections\");\n\t\t}\n\n\t\t$this->conn_id = parent::db_connect(FALSE);\n\n\t\tif ( ! is_object($this->conn_id))\n\t\t{\n\t\t\treturn $this->conn_id;\n\t\t}\n\n\t\t// Determine how identifiers are escaped\n\t\t$query = $this->query('SELECT CASE WHEN (@@OPTIONS | 256) = @@OPTIONS THEN 1 ELSE 0 END AS qi');\n\t\t$query = $query->row_array();\n\t\t$this->_quoted_identifier = empty($query) ? FALSE : (bool) $query['qi'];\n\t\t$this->_escape_char = ($this->_quoted_identifier) ? '\"' : array('[', ']');\n\n\t\treturn $this->conn_id;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show table query\n\t *\n\t * Generates a platform-specific query string so that the table names can be fetched\n\t *\n\t * @param\tbool\t$prefix_limit\n\t * @return\tstring\n\t */\n\tprotected function _list_tables($prefix_limit = FALSE)\n\t{\n\t\t$sql = 'SELECT '.$this->escape_identifiers('name')\n\t\t\t.' FROM '.$this->escape_identifiers('sysobjects')\n\t\t\t.' WHERE '.$this->escape_identifiers('type').\" = 'U'\";\n\n\t\tif ($prefix_limit === TRUE && $this->dbprefix !== '')\n\t\t{\n\t\t\t$sql .= ' AND '.$this->escape_identifiers('name').\" LIKE '\".$this->escape_like_str($this->dbprefix).\"%' \"\n\t\t\t\t.sprintf($this->_like_escape_str, $this->_like_escape_chr);\n\t\t}\n\n\t\treturn $sql.' ORDER BY '.$this->escape_identifiers('name');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show column query\n\t *\n\t * Generates a platform-specific query string so that the column names can be fetched\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _list_columns($table = '')\n\t{\n\t\treturn 'SELECT COLUMN_NAME\n\t\t\tFROM INFORMATION_SCHEMA.Columns\n\t\t\tWHERE UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns an object with field data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tarray\n\t */\n\tpublic function field_data($table)\n\t{\n\t\t$sql = 'SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, COLUMN_DEFAULT\n\t\t\tFROM INFORMATION_SCHEMA.Columns\n\t\t\tWHERE UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));\n\n\t\tif (($query = $this->query($sql)) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\t\t$query = $query->result_object();\n\n\t\t$retval = array();\n\t\tfor ($i = 0, $c = count($query); $i < $c; $i++)\n\t\t{\n\t\t\t$retval[$i]\t\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t\t= $query[$i]->COLUMN_NAME;\n\t\t\t$retval[$i]->type\t\t= $query[$i]->DATA_TYPE;\n\t\t\t$retval[$i]->max_length\t\t= ($query[$i]->CHARACTER_MAXIMUM_LENGTH > 0) ? $query[$i]->CHARACTER_MAXIMUM_LENGTH : $query[$i]->NUMERIC_PRECISION;\n\t\t\t$retval[$i]->default\t\t= $query[$i]->COLUMN_DEFAULT;\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Update statement\n\t *\n\t * Generates a platform-specific update string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @param\tarray\t$values\n\t * @return\tstring\n\t */\n\tprotected function _update($table, $values)\n\t{\n\t\t$this->qb_limit = FALSE;\n\t\t$this->qb_orderby = array();\n\t\treturn parent::_update($table, $values);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Delete statement\n\t *\n\t * Generates a platform-specific delete string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _delete($table)\n\t{\n\t\tif ($this->qb_limit)\n\t\t{\n\t\t\treturn 'WITH ci_delete AS (SELECT TOP '.$this->qb_limit.' * FROM '.$table.$this->_compile_wh('qb_where').') DELETE FROM ci_delete';\n\t\t}\n\n\t\treturn parent::_delete($table);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * LIMIT\n\t *\n\t * Generates a platform-specific LIMIT clause\n\t *\n\t * @param\tstring\t$sql\tSQL Query\n\t * @return\tstring\n\t */\n\tprotected function _limit($sql)\n\t{\n\t\t$limit = $this->qb_offset + $this->qb_limit;\n\n\t\t// As of SQL Server 2005 (9.0.*) ROW_NUMBER() is supported,\n\t\t// however an ORDER BY clause is required for it to work\n\t\tif (version_compare($this->version(), '9', '>=') && $this->qb_offset && ! empty($this->qb_orderby))\n\t\t{\n\t\t\t$orderby = $this->_compile_order_by();\n\n\t\t\t// We have to strip the ORDER BY clause\n\t\t\t$sql = trim(substr($sql, 0, strrpos($sql, $orderby)));\n\n\t\t\t// Get the fields to select from our subquery, so that we can avoid CI_rownum appearing in the actual results\n\t\t\tif (count($this->qb_select) === 0 OR strpos(implode(',', $this->qb_select), '*') !== FALSE)\n\t\t\t{\n\t\t\t\t$select = '*'; // Inevitable\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Use only field names and their aliases, everything else is out of our scope.\n\t\t\t\t$select = array();\n\t\t\t\t$field_regexp = ($this->_quoted_identifier)\n\t\t\t\t\t? '(\"[^\\\"]+\")' : '(\\[[^\\]]+\\])';\n\t\t\t\tfor ($i = 0, $c = count($this->qb_select); $i < $c; $i++)\n\t\t\t\t{\n\t\t\t\t\t$select[] = preg_match('/(?:\\s|\\.)'.$field_regexp.'$/i', $this->qb_select[$i], $m)\n\t\t\t\t\t\t? $m[1] : $this->qb_select[$i];\n\t\t\t\t}\n\t\t\t\t$select = implode(', ', $select);\n\t\t\t}\n\n\t\t\treturn 'SELECT '.$select.\" FROM (\\n\\n\"\n\t\t\t\t.preg_replace('/^(SELECT( DISTINCT)?)/i', '\\\\1 ROW_NUMBER() OVER('.trim($orderby).') AS '.$this->escape_identifiers('CI_rownum').', ', $sql)\n\t\t\t\t.\"\\n\\n) \".$this->escape_identifiers('CI_subquery')\n\t\t\t\t.\"\\nWHERE \".$this->escape_identifiers('CI_rownum').' BETWEEN '.($this->qb_offset + 1).' AND '.$limit;\n\t\t}\n\n\t\treturn preg_replace('/(^\\SELECT (DISTINCT)?)/i','\\\\1 TOP '.$limit.' ', $sql);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert batch statement\n\t *\n\t * Generates a platform-specific insert string from the supplied data.\n\t *\n\t * @param\tstring\t$table\tTable name\n\t * @param\tarray\t$keys\tINSERT keys\n\t * @param\tarray\t$values\tINSERT values\n\t * @return\tstring|bool\n\t */\n\tprotected function _insert_batch($table, $keys, $values)\n\t{\n\t\t// Multiple-value inserts are only supported as of SQL Server 2008\n\t\tif (version_compare($this->version(), '10', '>='))\n\t\t{\n\t\t\treturn parent::_insert_batch($table, $keys, $values);\n\t\t}\n\n\t\treturn ($this->db_debug) ? $this->display_error('db_unsupported_feature') : FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Database version number\n\t *\n\t * @return      string\n\t */\n\tpublic function version()\n\t{\n\t\tif (isset($this->data_cache['version']))\n\t\t{\n\t\t\treturn $this->data_cache['version'];\n\t\t}\n\n\t\treturn $this->data_cache['version'] = $this->conn_id->query(\"SELECT SERVERPROPERTY('ProductVersion') AS ver\")->fetchColumn(0);\n\t}\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_dblib_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO DBLIB Forge Class\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_dblib_forge extends CI_DB_pdo_forge {\n\n\t/**\n\t * CREATE TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_table_if\t= \"IF NOT EXISTS (SELECT * FROM sysobjects WHERE ID = object_id(N'%s') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)\\nCREATE TABLE\";\n\n\t/**\n\t * DROP TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_drop_table_if\t= \"IF EXISTS (SELECT * FROM sysobjects WHERE ID = object_id(N'%s') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)\\nDROP TABLE\";\n\n\t/**\n\t * UNSIGNED support\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_unsigned\t\t= array(\n\t\t'TINYINT'\t=> 'SMALLINT',\n\t\t'SMALLINT'\t=> 'INT',\n\t\t'INT'\t\t=> 'BIGINT',\n\t\t'REAL'\t\t=> 'FLOAT'\n\t);\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ALTER TABLE\n\t *\n\t * @param\tstring\t$alter_type\tALTER type\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tmixed\t$field\t\tColumn definition\n\t * @return\tstring|string[]\n\t */\n\tprotected function _alter_table($alter_type, $table, $field)\n\t{\n\t\tif (in_array($alter_type, array('ADD', 'DROP'), TRUE))\n\t\t{\n\t\t\treturn parent::_alter_table($alter_type, $table, $field);\n\t\t}\n\n\t\t$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' ALTER COLUMN ';\n\t\t$sqls = array();\n\t\tfor ($i = 0, $c = count($field); $i < $c; $i++)\n\t\t{\n\t\t\t$sqls[] = $sql.$this->_process_column($field[$i]);\n\t\t}\n\n\t\treturn $sqls;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute TYPE\n\t *\n\t * Performs a data type mapping between different databases.\n\t *\n\t * @param\tarray\t&$attributes\n\t * @return\tvoid\n\t */\n\tprotected function _attr_type(&$attributes)\n\t{\n\t\tif (isset($attributes['CONSTRAINT']) && strpos($attributes['TYPE'], 'INT') !== FALSE)\n\t\t{\n\t\t\tunset($attributes['CONSTRAINT']);\n\t\t}\n\n\t\tswitch (strtoupper($attributes['TYPE']))\n\t\t{\n\t\t\tcase 'MEDIUMINT':\n\t\t\t\t$attributes['TYPE'] = 'INTEGER';\n\t\t\t\t$attributes['UNSIGNED'] = FALSE;\n\t\t\t\treturn;\n\t\t\tcase 'INTEGER':\n\t\t\t\t$attributes['TYPE'] = 'INT';\n\t\t\t\treturn;\n\t\t\tdefault: return;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute AUTO_INCREMENT\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_auto_increment(&$attributes, &$field)\n\t{\n\t\tif ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE)\n\t\t{\n\t\t\t$field['auto_increment'] = ' IDENTITY(1,1)';\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO Firebird Database Adapter Class\n *\n * Note: _DB is an extender class that the app controller\n * creates dynamically based on whether the query builder\n * class is being used or not.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_firebird_driver extends CI_DB_pdo_driver {\n\n\t/**\n\t * Sub-driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $subdriver = 'firebird';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ORDER BY random keyword\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_random_keyword = array('RAND()', 'RAND()');\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * Builds the DSN if not already set.\n\t *\n\t * @param\tarray\t$params\n\t * @return\tvoid\n\t */\n\tpublic function __construct($params)\n\t{\n\t\tparent::__construct($params);\n\n\t\tif (empty($this->dsn))\n\t\t{\n\t\t\t$this->dsn = 'firebird:';\n\n\t\t\tif ( ! empty($this->database))\n\t\t\t{\n\t\t\t\t$this->dsn .= 'dbname='.$this->database;\n\t\t\t}\n\t\t\telseif ( ! empty($this->hostname))\n\t\t\t{\n\t\t\t\t$this->dsn .= 'dbname='.$this->hostname;\n\t\t\t}\n\n\t\t\tempty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;\n\t\t\tempty($this->role) OR $this->dsn .= ';role='.$this->role;\n\t\t}\n\t\telseif ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 9) === FALSE)\n\t\t{\n\t\t\t$this->dsn .= ';charset='.$this->char_set;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show table query\n\t *\n\t * Generates a platform-specific query string so that the table names can be fetched\n\t *\n\t * @param\tbool\t$prefix_limit\n\t * @return\tstring\n\t */\n\tprotected function _list_tables($prefix_limit = FALSE)\n\t{\n\t\t$sql = 'SELECT \"RDB$RELATION_NAME\" FROM \"RDB$RELATIONS\" WHERE \"RDB$RELATION_NAME\" NOT LIKE \\'RDB$%\\' AND \"RDB$RELATION_NAME\" NOT LIKE \\'MON$%\\'';\n\n\t\tif ($prefix_limit === TRUE && $this->dbprefix !== '')\n\t\t{\n\t\t\treturn $sql.' AND \"RDB$RELATION_NAME\" LIKE \\''.$this->escape_like_str($this->dbprefix).\"%' \"\n\t\t\t\t.sprintf($this->_like_escape_str, $this->_like_escape_chr);\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show column query\n\t *\n\t * Generates a platform-specific query string so that the column names can be fetched\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _list_columns($table = '')\n\t{\n\t\treturn 'SELECT \"RDB$FIELD_NAME\" FROM \"RDB$RELATION_FIELDS\" WHERE \"RDB$RELATION_NAME\" = '.$this->escape($table);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns an object with field data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tarray\n\t */\n\tpublic function field_data($table)\n\t{\n\t\t$sql = 'SELECT \"rfields\".\"RDB$FIELD_NAME\" AS \"name\",\n\t\t\t\tCASE \"fields\".\"RDB$FIELD_TYPE\"\n\t\t\t\t\tWHEN 7 THEN \\'SMALLINT\\'\n\t\t\t\t\tWHEN 8 THEN \\'INTEGER\\'\n\t\t\t\t\tWHEN 9 THEN \\'QUAD\\'\n\t\t\t\t\tWHEN 10 THEN \\'FLOAT\\'\n\t\t\t\t\tWHEN 11 THEN \\'DFLOAT\\'\n\t\t\t\t\tWHEN 12 THEN \\'DATE\\'\n\t\t\t\t\tWHEN 13 THEN \\'TIME\\'\n\t\t\t\t\tWHEN 14 THEN \\'CHAR\\'\n\t\t\t\t\tWHEN 16 THEN \\'INT64\\'\n\t\t\t\t\tWHEN 27 THEN \\'DOUBLE\\'\n\t\t\t\t\tWHEN 35 THEN \\'TIMESTAMP\\'\n\t\t\t\t\tWHEN 37 THEN \\'VARCHAR\\'\n\t\t\t\t\tWHEN 40 THEN \\'CSTRING\\'\n\t\t\t\t\tWHEN 261 THEN \\'BLOB\\'\n\t\t\t\t\tELSE NULL\n\t\t\t\tEND AS \"type\",\n\t\t\t\t\"fields\".\"RDB$FIELD_LENGTH\" AS \"max_length\",\n\t\t\t\t\"rfields\".\"RDB$DEFAULT_VALUE\" AS \"default\"\n\t\t\tFROM \"RDB$RELATION_FIELDS\" \"rfields\"\n\t\t\t\tJOIN \"RDB$FIELDS\" \"fields\" ON \"rfields\".\"RDB$FIELD_SOURCE\" = \"fields\".\"RDB$FIELD_NAME\"\n\t\t\tWHERE \"rfields\".\"RDB$RELATION_NAME\" = '.$this->escape($table).'\n\t\t\tORDER BY \"rfields\".\"RDB$FIELD_POSITION\"';\n\n\t\treturn (($query = $this->query($sql)) !== FALSE)\n\t\t\t? $query->result_object()\n\t\t\t: FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Update statement\n\t *\n\t * Generates a platform-specific update string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @param\tarray\t$values\n\t * @return\tstring\n\t */\n\tprotected function _update($table, $values)\n\t{\n\t\t$this->qb_limit = FALSE;\n\t\treturn parent::_update($table, $values);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Truncate statement\n\t *\n\t * Generates a platform-specific truncate string from the supplied data\n\t *\n\t * If the database does not support the TRUNCATE statement,\n\t * then this method maps to 'DELETE FROM table'\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _truncate($table)\n\t{\n\t\treturn 'DELETE FROM '.$table;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Delete statement\n\t *\n\t * Generates a platform-specific delete string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _delete($table)\n\t{\n\t\t$this->qb_limit = FALSE;\n\t\treturn parent::_delete($table);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * LIMIT\n\t *\n\t * Generates a platform-specific LIMIT clause\n\t *\n\t * @param\tstring\t$sql\tSQL Query\n\t * @return\tstring\n\t */\n\tprotected function _limit($sql)\n\t{\n\t\t// Limit clause depends on if Interbase or Firebird\n\t\tif (stripos($this->version(), 'firebird') !== FALSE)\n\t\t{\n\t\t\t$select = 'FIRST '.$this->qb_limit\n\t\t\t\t.($this->qb_offset > 0 ? ' SKIP '.$this->qb_offset : '');\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$select = 'ROWS '\n\t\t\t\t.($this->qb_offset > 0 ? $this->qb_offset.' TO '.($this->qb_limit + $this->qb_offset) : $this->qb_limit);\n\t\t}\n\n\t\treturn preg_replace('`SELECT`i', 'SELECT '.$select, $sql);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert batch statement\n\t *\n\t * Generates a platform-specific insert string from the supplied data.\n\t *\n\t * @param\tstring\t$table\tTable name\n\t * @param\tarray\t$keys\tINSERT keys\n\t * @param\tarray\t$values\tINSERT values\n\t * @return\tstring|bool\n\t */\n\tprotected function _insert_batch($table, $keys, $values)\n\t{\n\t\treturn ($this->db_debug) ? $this->display_error('db_unsupported_feature') : FALSE;\n\t}\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_firebird_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO Firebird Forge Class\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_firebird_forge extends CI_DB_pdo_forge {\n\n\t/**\n\t * RENAME TABLE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_rename_table\t= FALSE;\n\n\t/**\n\t * UNSIGNED support\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_unsigned\t\t= array(\n\t\t'SMALLINT'\t=> 'INTEGER',\n\t\t'INTEGER'\t=> 'INT64',\n\t\t'FLOAT'\t\t=> 'DOUBLE PRECISION'\n\t);\n\n\t/**\n\t * NULL value representation in CREATE/ALTER TABLE statements\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_null\t\t= 'NULL';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Create database\n\t *\n\t * @param\tstring\t$db_name\n\t * @return\tstring\n\t */\n\tpublic function create_database($db_name)\n\t{\n\t\t// Firebird databases are flat files, so a path is required\n\n\t\t// Hostname is needed for remote access\n\t\tempty($this->db->hostname) OR $db_name = $this->hostname.':'.$db_name;\n\n\t\treturn parent::create_database('\"'.$db_name.'\"');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Drop database\n\t *\n\t * @param\tstring\t$db_name\t(ignored)\n\t * @return\tbool\n\t */\n\tpublic function drop_database($db_name)\n\t{\n\t\tif ( ! ibase_drop_db($this->conn_id))\n\t\t{\n\t\t\treturn ($this->db->db_debug) ? $this->db->display_error('db_unable_to_drop') : FALSE;\n\t\t}\n\t\telseif ( ! empty($this->db->data_cache['db_names']))\n\t\t{\n\t\t\t$key = array_search(strtolower($this->db->database), array_map('strtolower', $this->db->data_cache['db_names']), TRUE);\n\t\t\tif ($key !== FALSE)\n\t\t\t{\n\t\t\t\tunset($this->db->data_cache['db_names'][$key]);\n\t\t\t}\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ALTER TABLE\n\t *\n\t * @param\tstring\t$alter_type\tALTER type\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tmixed\t$field\t\tColumn definition\n\t * @return\tstring|string[]\n\t */\n\tprotected function _alter_table($alter_type, $table, $field)\n\t{\n\t\tif (in_array($alter_type, array('DROP', 'ADD'), TRUE))\n\t\t{\n\t\t\treturn parent::_alter_table($alter_type, $table, $field);\n\t\t}\n\n\t\t$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table);\n\t\t$sqls = array();\n\t\tfor ($i = 0, $c = count($field); $i < $c; $i++)\n\t\t{\n\t\t\tif ($field[$i]['_literal'] !== FALSE)\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\tif (isset($field[$i]['type']))\n\t\t\t{\n\t\t\t\t$sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identifiers($field[$i]['name'])\n\t\t\t\t\t.' TYPE '.$field[$i]['type'].$field[$i]['length'];\n\t\t\t}\n\n\t\t\tif ( ! empty($field[$i]['default']))\n\t\t\t{\n\t\t\t\t$sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identifiers($field[$i]['name'])\n\t\t\t\t\t.' SET '.$field[$i]['default'];\n\t\t\t}\n\n\t\t\tif (isset($field[$i]['null']))\n\t\t\t{\n\t\t\t\t$sqls[] = 'UPDATE \"RDB$RELATION_FIELDS\" SET \"RDB$NULL_FLAG\" = '\n\t\t\t\t\t.($field[$i]['null'] === TRUE ? 'NULL' : '1')\n\t\t\t\t\t.' WHERE \"RDB$FIELD_NAME\" = '.$this->db->escape($field[$i]['name'])\n\t\t\t\t\t.' AND \"RDB$RELATION_NAME\" = '.$this->db->escape($table);\n\t\t\t}\n\n\t\t\tif ( ! empty($field[$i]['new_name']))\n\t\t\t{\n\t\t\t\t$sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identifiers($field[$i]['name'])\n\t\t\t\t\t.' TO '.$this->db->escape_identifiers($field[$i]['new_name']);\n\t\t\t}\n\t\t}\n\n\t\treturn $sqls;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Process column\n\t *\n\t * @param\tarray\t$field\n\t * @return\tstring\n\t */\n\tprotected function _process_column($field)\n\t{\n\t\treturn $this->db->escape_identifiers($field['name'])\n\t\t\t.' '.$field['type'].$field['length']\n\t\t\t.$field['null']\n\t\t\t.$field['unique']\n\t\t\t.$field['default'];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute TYPE\n\t *\n\t * Performs a data type mapping between different databases.\n\t *\n\t * @param\tarray\t&$attributes\n\t * @return\tvoid\n\t */\n\tprotected function _attr_type(&$attributes)\n\t{\n\t\tswitch (strtoupper($attributes['TYPE']))\n\t\t{\n\t\t\tcase 'TINYINT':\n\t\t\t\t$attributes['TYPE'] = 'SMALLINT';\n\t\t\t\t$attributes['UNSIGNED'] = FALSE;\n\t\t\t\treturn;\n\t\t\tcase 'MEDIUMINT':\n\t\t\t\t$attributes['TYPE'] = 'INTEGER';\n\t\t\t\t$attributes['UNSIGNED'] = FALSE;\n\t\t\t\treturn;\n\t\t\tcase 'INT':\n\t\t\t\t$attributes['TYPE'] = 'INTEGER';\n\t\t\t\treturn;\n\t\t\tcase 'BIGINT':\n\t\t\t\t$attributes['TYPE'] = 'INT64';\n\t\t\t\treturn;\n\t\t\tdefault: return;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute AUTO_INCREMENT\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_auto_increment(&$attributes, &$field)\n\t{\n\t\t// Not supported\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_ibm_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO IBM DB2 Database Adapter Class\n *\n * Note: _DB is an extender class that the app controller\n * creates dynamically based on whether the query builder\n * class is being used or not.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_ibm_driver extends CI_DB_pdo_driver {\n\n\t/**\n\t * Sub-driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $subdriver = 'ibm';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * Builds the DSN if not already set.\n\t *\n\t * @param\tarray\t$params\n\t * @return\tvoid\n\t */\n\tpublic function __construct($params)\n\t{\n\t\tparent::__construct($params);\n\n\t\tif (empty($this->dsn))\n\t\t{\n\t\t\t$this->dsn = 'ibm:';\n\n\t\t\t// Pre-defined DSN\n\t\t\tif (empty($this->hostname) && empty($this->HOSTNAME) && empty($this->port) && empty($this->PORT))\n\t\t\t{\n\t\t\t\tif (isset($this->DSN))\n\t\t\t\t{\n\t\t\t\t\t$this->dsn .= 'DSN='.$this->DSN;\n\t\t\t\t}\n\t\t\t\telseif ( ! empty($this->database))\n\t\t\t\t{\n\t\t\t\t\t$this->dsn .= 'DSN='.$this->database;\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t$this->dsn .= 'DRIVER='.(isset($this->DRIVER) ? '{'.$this->DRIVER.'}' : '{IBM DB2 ODBC DRIVER}').';';\n\n\t\t\tif (isset($this->DATABASE))\n\t\t\t{\n\t\t\t\t$this->dsn .= 'DATABASE='.$this->DATABASE.';';\n\t\t\t}\n\t\t\telseif ( ! empty($this->database))\n\t\t\t{\n\t\t\t\t$this->dsn .= 'DATABASE='.$this->database.';';\n\t\t\t}\n\n\t\t\tif (isset($this->HOSTNAME))\n\t\t\t{\n\t\t\t\t$this->dsn .= 'HOSTNAME='.$this->HOSTNAME.';';\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$this->dsn .= 'HOSTNAME='.(empty($this->hostname) ? '127.0.0.1;' : $this->hostname.';');\n\t\t\t}\n\n\t\t\tif (isset($this->PORT))\n\t\t\t{\n\t\t\t\t$this->dsn .= 'PORT='.$this->port.';';\n\t\t\t}\n\t\t\telseif ( ! empty($this->port))\n\t\t\t{\n\t\t\t\t$this->dsn .= ';PORT='.$this->port.';';\n\t\t\t}\n\n\t\t\t$this->dsn .= 'PROTOCOL='.(isset($this->PROTOCOL) ? $this->PROTOCOL.';' : 'TCPIP;');\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show table query\n\t *\n\t * Generates a platform-specific query string so that the table names can be fetched\n\t *\n\t * @param\tbool\t$prefix_limit\n\t * @return\tstring\n\t */\n\tprotected function _list_tables($prefix_limit = FALSE)\n\t{\n\t\t$sql = 'SELECT \"tabname\" FROM \"syscat\".\"tables\"\n\t\t\tWHERE \"type\" = \\'T\\' AND LOWER(\"tabschema\") = '.$this->escape(strtolower($this->database));\n\n\t\tif ($prefix_limit === TRUE && $this->dbprefix !== '')\n\t\t{\n\t\t\t$sql .= ' AND \"tabname\" LIKE \\''.$this->escape_like_str($this->dbprefix).\"%' \"\n\t\t\t\t.sprintf($this->_like_escape_str, $this->_like_escape_chr);\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show column query\n\t *\n\t * Generates a platform-specific query string so that the column names can be fetched\n\t *\n\t * @param\tstring\t$table\n\t * @return\tarray\n\t */\n\tprotected function _list_columns($table = '')\n\t{\n\t\treturn 'SELECT \"colname\" FROM \"syscat\".\"columns\"\n\t\t\tWHERE LOWER(\"tabschema\") = '.$this->escape(strtolower($this->database)).'\n\t\t\t\tAND LOWER(\"tabname\") = '.$this->escape(strtolower($table));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns an object with field data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tarray\n\t */\n\tpublic function field_data($table)\n\t{\n\t\t$sql = 'SELECT \"colname\" AS \"name\", \"typename\" AS \"type\", \"default\" AS \"default\", \"length\" AS \"max_length\",\n\t\t\t\tCASE \"keyseq\" WHEN NULL THEN 0 ELSE 1 END AS \"primary_key\"\n\t\t\tFROM \"syscat\".\"columns\"\n\t\t\tWHERE LOWER(\"tabschema\") = '.$this->escape(strtolower($this->database)).'\n\t\t\t\tAND LOWER(\"tabname\") = '.$this->escape(strtolower($table)).'\n\t\t\tORDER BY \"colno\"';\n\n\t\treturn (($query = $this->query($sql)) !== FALSE)\n\t\t\t? $query->result_object()\n\t\t\t: FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Update statement\n\t *\n\t * Generates a platform-specific update string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @param\tarray\t$values\n\t * @return\tstring\n\t */\n\tprotected function _update($table, $values)\n\t{\n\t\t$this->qb_limit = FALSE;\n\t\t$this->qb_orderby = array();\n\t\treturn parent::_update($table, $values);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Delete statement\n\t *\n\t * Generates a platform-specific delete string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _delete($table)\n\t{\n\t\t$this->qb_limit = FALSE;\n\t\treturn parent::_delete($table);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * LIMIT\n\t *\n\t * Generates a platform-specific LIMIT clause\n\t *\n\t * @param\tstring\t$sql\tSQL Query\n\t * @return\tstring\n\t */\n\tprotected function _limit($sql)\n\t{\n\t\t$sql .= ' FETCH FIRST '.($this->qb_limit + $this->qb_offset).' ROWS ONLY';\n\n\t\treturn ($this->qb_offset)\n\t\t\t? 'SELECT * FROM ('.$sql.') WHERE rownum > '.$this->qb_offset\n\t\t\t: $sql;\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_ibm_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO IBM DB2 Forge Class\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_ibm_forge extends CI_DB_pdo_forge {\n\n\t/**\n\t * RENAME TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_rename_table\t= 'RENAME TABLE %s TO %s';\n\n\t/**\n\t * UNSIGNED support\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_unsigned\t\t= array(\n\t\t'SMALLINT'\t=> 'INTEGER',\n\t\t'INT'\t\t=> 'BIGINT',\n\t\t'INTEGER'\t=> 'BIGINT'\n\t);\n\n\t/**\n\t * DEFAULT value representation in CREATE/ALTER TABLE statements\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_default\t\t= FALSE;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ALTER TABLE\n\t *\n\t * @param\tstring\t$alter_type\tALTER type\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tmixed\t$field\t\tColumn definition\n\t * @return\tstring|string[]\n\t */\n\tprotected function _alter_table($alter_type, $table, $field)\n\t{\n\t\tif ($alter_type === 'CHANGE')\n\t\t{\n\t\t\t$alter_type = 'MODIFY';\n\t\t}\n\n\t\treturn parent::_alter_table($alter_type, $table, $field);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute TYPE\n\t *\n\t * Performs a data type mapping between different databases.\n\t *\n\t * @param\tarray\t&$attributes\n\t * @return\tvoid\n\t */\n\tprotected function _attr_type(&$attributes)\n\t{\n\t\tswitch (strtoupper($attributes['TYPE']))\n\t\t{\n\t\t\tcase 'TINYINT':\n\t\t\t\t$attributes['TYPE'] = 'SMALLINT';\n\t\t\t\t$attributes['UNSIGNED'] = FALSE;\n\t\t\t\treturn;\n\t\t\tcase 'MEDIUMINT':\n\t\t\t\t$attributes['TYPE'] = 'INTEGER';\n\t\t\t\t$attributes['UNSIGNED'] = FALSE;\n\t\t\t\treturn;\n\t\t\tdefault: return;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute UNIQUE\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_unique(&$attributes, &$field)\n\t{\n\t\tif ( ! empty($attributes['UNIQUE']) && $attributes['UNIQUE'] === TRUE)\n\t\t{\n\t\t\t$field['unique'] = ' UNIQUE';\n\n\t\t\t// UNIQUE must be used with NOT NULL\n\t\t\t$field['null'] = ' NOT NULL';\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute AUTO_INCREMENT\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_auto_increment(&$attributes, &$field)\n\t{\n\t\t// Not supported\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_informix_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO Informix Database Adapter Class\n *\n * Note: _DB is an extender class that the app controller\n * creates dynamically based on whether the query builder\n * class is being used or not.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_informix_driver extends CI_DB_pdo_driver {\n\n\t/**\n\t * Sub-driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $subdriver = 'informix';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ORDER BY random keyword\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_random_keyword = array('ASC', 'ASC'); // Currently not supported\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * Builds the DSN if not already set.\n\t *\n\t * @param\tarray\t$params\n\t * @return\tvoid\n\t */\n\tpublic function __construct($params)\n\t{\n\t\tparent::__construct($params);\n\n\t\tif (empty($this->dsn))\n\t\t{\n\t\t\t$this->dsn = 'informix:';\n\n\t\t\t// Pre-defined DSN\n\t\t\tif (empty($this->hostname) && empty($this->host) && empty($this->port) && empty($this->service))\n\t\t\t{\n\t\t\t\tif (isset($this->DSN))\n\t\t\t\t{\n\t\t\t\t\t$this->dsn .= 'DSN='.$this->DSN;\n\t\t\t\t}\n\t\t\t\telseif ( ! empty($this->database))\n\t\t\t\t{\n\t\t\t\t\t$this->dsn .= 'DSN='.$this->database;\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (isset($this->host))\n\t\t\t{\n\t\t\t\t$this->dsn .= 'host='.$this->host;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$this->dsn .= 'host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);\n\t\t\t}\n\n\t\t\tif (isset($this->service))\n\t\t\t{\n\t\t\t\t$this->dsn .= '; service='.$this->service;\n\t\t\t}\n\t\t\telseif ( ! empty($this->port))\n\t\t\t{\n\t\t\t\t$this->dsn .= '; service='.$this->port;\n\t\t\t}\n\n\t\t\tempty($this->database) OR $this->dsn .= '; database='.$this->database;\n\t\t\tempty($this->server) OR $this->dsn .= '; server='.$this->server;\n\n\t\t\t$this->dsn .= '; protocol='.(isset($this->protocol) ? $this->protocol : 'onsoctcp')\n\t\t\t\t.'; EnableScrollableCursors=1';\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show table query\n\t *\n\t * Generates a platform-specific query string so that the table names can be fetched\n\t *\n\t * @param\tbool\t$prefix_limit\n\t * @return\tstring\n\t */\n\tprotected function _list_tables($prefix_limit = FALSE)\n\t{\n\t\t$sql = 'SELECT \"tabname\" FROM \"systables\"\n\t\t\tWHERE \"tabid\" > 99 AND \"tabtype\" = \\'T\\' AND LOWER(\"owner\") = '.$this->escape(strtolower($this->username));\n\n\t\tif ($prefix_limit === TRUE && $this->dbprefix !== '')\n\t\t{\n\t\t\t$sql .= ' AND \"tabname\" LIKE \\''.$this->escape_like_str($this->dbprefix).\"%' \"\n\t\t\t\t.sprintf($this->_like_escape_str, $this->_like_escape_chr);\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show column query\n\t *\n\t * Generates a platform-specific query string so that the column names can be fetched\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _list_columns($table = '')\n\t{\n\t\tif (strpos($table, '.') !== FALSE)\n\t\t{\n\t\t\tsscanf($table, '%[^.].%s', $owner, $table);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$owner = $this->username;\n\t\t}\n\n\t\treturn 'SELECT \"colname\" FROM \"systables\", \"syscolumns\"\n\t\t\tWHERE \"systables\".\"tabid\" = \"syscolumns\".\"tabid\"\n\t\t\t\tAND \"systables\".\"tabtype\" = \\'T\\'\n\t\t\t\tAND LOWER(\"systables\".\"owner\") = '.$this->escape(strtolower($owner)).'\n\t\t\t\tAND LOWER(\"systables\".\"tabname\") = '.$this->escape(strtolower($table));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns an object with field data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tarray\n\t */\n\tpublic function field_data($table)\n\t{\n\t\t$sql = 'SELECT \"syscolumns\".\"colname\" AS \"name\",\n\t\t\t\tCASE \"syscolumns\".\"coltype\"\n\t\t\t\t\tWHEN 0 THEN \\'CHAR\\'\n\t\t\t\t\tWHEN 1 THEN \\'SMALLINT\\'\n\t\t\t\t\tWHEN 2 THEN \\'INTEGER\\'\n\t\t\t\t\tWHEN 3 THEN \\'FLOAT\\'\n\t\t\t\t\tWHEN 4 THEN \\'SMALLFLOAT\\'\n\t\t\t\t\tWHEN 5 THEN \\'DECIMAL\\'\n\t\t\t\t\tWHEN 6 THEN \\'SERIAL\\'\n\t\t\t\t\tWHEN 7 THEN \\'DATE\\'\n\t\t\t\t\tWHEN 8 THEN \\'MONEY\\'\n\t\t\t\t\tWHEN 9 THEN \\'NULL\\'\n\t\t\t\t\tWHEN 10 THEN \\'DATETIME\\'\n\t\t\t\t\tWHEN 11 THEN \\'BYTE\\'\n\t\t\t\t\tWHEN 12 THEN \\'TEXT\\'\n\t\t\t\t\tWHEN 13 THEN \\'VARCHAR\\'\n\t\t\t\t\tWHEN 14 THEN \\'INTERVAL\\'\n\t\t\t\t\tWHEN 15 THEN \\'NCHAR\\'\n\t\t\t\t\tWHEN 16 THEN \\'NVARCHAR\\'\n\t\t\t\t\tWHEN 17 THEN \\'INT8\\'\n\t\t\t\t\tWHEN 18 THEN \\'SERIAL8\\'\n\t\t\t\t\tWHEN 19 THEN \\'SET\\'\n\t\t\t\t\tWHEN 20 THEN \\'MULTISET\\'\n\t\t\t\t\tWHEN 21 THEN \\'LIST\\'\n\t\t\t\t\tWHEN 22 THEN \\'Unnamed ROW\\'\n\t\t\t\t\tWHEN 40 THEN \\'LVARCHAR\\'\n\t\t\t\t\tWHEN 41 THEN \\'BLOB/CLOB/BOOLEAN\\'\n\t\t\t\t\tWHEN 4118 THEN \\'Named ROW\\'\n\t\t\t\t\tELSE \"syscolumns\".\"coltype\"\n\t\t\t\tEND AS \"type\",\n\t\t\t\t\"syscolumns\".\"collength\" as \"max_length\",\n\t\t\t\tCASE \"sysdefaults\".\"type\"\n\t\t\t\t\tWHEN \\'L\\' THEN \"sysdefaults\".\"default\"\n\t\t\t\t\tELSE NULL\n\t\t\t\tEND AS \"default\"\n\t\t\tFROM \"syscolumns\", \"systables\", \"sysdefaults\"\n\t\t\tWHERE \"syscolumns\".\"tabid\" = \"systables\".\"tabid\"\n\t\t\t\tAND \"systables\".\"tabid\" = \"sysdefaults\".\"tabid\"\n\t\t\t\tAND \"syscolumns\".\"colno\" = \"sysdefaults\".\"colno\"\n\t\t\t\tAND \"systables\".\"tabtype\" = \\'T\\'\n\t\t\t\tAND LOWER(\"systables\".\"owner\") = '.$this->escape(strtolower($this->username)).'\n\t\t\t\tAND LOWER(\"systables\".\"tabname\") = '.$this->escape(strtolower($table)).'\n\t\t\tORDER BY \"syscolumns\".\"colno\"';\n\n\t\treturn (($query = $this->query($sql)) !== FALSE)\n\t\t\t? $query->result_object()\n\t\t\t: FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Update statement\n\t *\n\t * Generates a platform-specific update string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @param\tarray\t$values\n\t * @return\tstring\n\t */\n\tprotected function _update($table, $values)\n\t{\n\t\t$this->qb_limit = FALSE;\n\t\t$this->qb_orderby = array();\n\t\treturn parent::_update($table, $values);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Truncate statement\n\t *\n\t * Generates a platform-specific truncate string from the supplied data\n\t *\n\t * If the database does not support the TRUNCATE statement,\n\t * then this method maps to 'DELETE FROM table'\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _truncate($table)\n\t{\n\t\treturn 'TRUNCATE TABLE ONLY '.$table;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Delete statement\n\t *\n\t * Generates a platform-specific delete string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _delete($table)\n\t{\n\t\t$this->qb_limit = FALSE;\n\t\treturn parent::_delete($table);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * LIMIT\n\t *\n\t * Generates a platform-specific LIMIT clause\n\t *\n\t * @param\tstring\t$sql\t$SQL Query\n\t * @return\tstring\n\t */\n\tprotected function _limit($sql)\n\t{\n\t\t$select = 'SELECT '.($this->qb_offset ? 'SKIP '.$this->qb_offset : '').'FIRST '.$this->qb_limit.' ';\n\t\treturn preg_replace('/^(SELECT\\s)/i', $select, $sql, 1);\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_informix_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO Informix Forge Class\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_informix_forge extends CI_DB_pdo_forge {\n\n\t/**\n\t * RENAME TABLE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_rename_table\t= 'RENAME TABLE %s TO %s';\n\n\t/**\n\t * UNSIGNED support\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_unsigned\t\t= array(\n\t\t'SMALLINT'\t=> 'INTEGER',\n\t\t'INT'\t\t=> 'BIGINT',\n\t\t'INTEGER'\t=> 'BIGINT',\n\t\t'REAL'\t\t=> 'DOUBLE PRECISION',\n\t\t'SMALLFLOAT'\t=> 'DOUBLE PRECISION'\n\t);\n\n\t/**\n\t * DEFAULT value representation in CREATE/ALTER TABLE statements\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_default\t\t= ', ';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ALTER TABLE\n\t *\n\t * @param\tstring\t$alter_type\tALTER type\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tmixed\t$field\t\tColumn definition\n\t * @return\tstring|string[]\n\t */\n\tprotected function _alter_table($alter_type, $table, $field)\n\t{\n\t\tif ($alter_type === 'CHANGE')\n\t\t{\n\t\t\t$alter_type = 'MODIFY';\n\t\t}\n\n\t\treturn parent::_alter_table($alter_type, $table, $field);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute TYPE\n\t *\n\t * Performs a data type mapping between different databases.\n\t *\n\t * @param\tarray\t&$attributes\n\t * @return\tvoid\n\t */\n\tprotected function _attr_type(&$attributes)\n\t{\n\t\tswitch (strtoupper($attributes['TYPE']))\n\t\t{\n\t\t\tcase 'TINYINT':\n\t\t\t\t$attributes['TYPE'] = 'SMALLINT';\n\t\t\t\t$attributes['UNSIGNED'] = FALSE;\n\t\t\t\treturn;\n\t\t\tcase 'MEDIUMINT':\n\t\t\t\t$attributes['TYPE'] = 'INTEGER';\n\t\t\t\t$attributes['UNSIGNED'] = FALSE;\n\t\t\t\treturn;\n\t\t\tcase 'BYTE':\n\t\t\tcase 'TEXT':\n\t\t\tcase 'BLOB':\n\t\t\tcase 'CLOB':\n\t\t\t\t$attributes['UNIQUE'] = FALSE;\n\t\t\t\tif (isset($attributes['DEFAULT']))\n\t\t\t\t{\n\t\t\t\t\tunset($attributes['DEFAULT']);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tdefault: return;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute UNIQUE\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_unique(&$attributes, &$field)\n\t{\n\t\tif ( ! empty($attributes['UNIQUE']) && $attributes['UNIQUE'] === TRUE)\n\t\t{\n\t\t\t$field['unique'] = ' UNIQUE CONSTRAINT '.$this->db->escape_identifiers($field['name']);\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute AUTO_INCREMENT\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_auto_increment(&$attributes, &$field)\n\t{\n\t\t// Not supported\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO MySQL Database Adapter Class\n *\n * Note: _DB is an extender class that the app controller\n * creates dynamically based on whether the query builder\n * class is being used or not.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_mysql_driver extends CI_DB_pdo_driver {\n\n\t/**\n\t * Sub-driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $subdriver = 'mysql';\n\n\t/**\n\t * Compression flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $compress = FALSE;\n\n\t/**\n\t * Strict ON flag\n\t *\n\t * Whether we're running in strict SQL mode.\n\t *\n\t * @var\tbool\n\t */\n\tpublic $stricton;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Identifier escape character\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_escape_char = '`';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * Builds the DSN if not already set.\n\t *\n\t * @param\tarray\t$params\n\t * @return\tvoid\n\t */\n\tpublic function __construct($params)\n\t{\n\t\tparent::__construct($params);\n\n\t\tif (empty($this->dsn))\n\t\t{\n\t\t\t$this->dsn = 'mysql:host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);\n\n\t\t\tempty($this->port) OR $this->dsn .= ';port='.$this->port;\n\t\t\tempty($this->database) OR $this->dsn .= ';dbname='.$this->database;\n\t\t\tempty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;\n\t\t}\n\t\telseif ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 6) === FALSE)\n\t\t{\n\t\t\t$this->dsn .= ';charset='.$this->char_set;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Database connection\n\t *\n\t * @param\tbool\t$persistent\n\t * @return\tobject\n\t */\n\tpublic function db_connect($persistent = FALSE)\n\t{\n\t\tif (isset($this->stricton))\n\t\t{\n\t\t\tif ($this->stricton)\n\t\t\t{\n\t\t\t\t$sql = 'CONCAT(@@sql_mode, \",\", \"STRICT_ALL_TABLES\")';\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$sql = 'REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(\n                                        @@sql_mode,\n                                        \"STRICT_ALL_TABLES,\", \"\"),\n                                        \",STRICT_ALL_TABLES\", \"\"),\n                                        \"STRICT_ALL_TABLES\", \"\"),\n                                        \"STRICT_TRANS_TABLES,\", \"\"),\n                                        \",STRICT_TRANS_TABLES\", \"\"),\n                                        \"STRICT_TRANS_TABLES\", \"\")';\n\t\t\t}\n\n\t\t\tif ( ! empty($sql))\n\t\t\t{\n\t\t\t\tif (empty($this->options[PDO::MYSQL_ATTR_INIT_COMMAND]))\n\t\t\t\t{\n\t\t\t\t\t$this->options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET SESSION sql_mode = '.$sql;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$this->options[PDO::MYSQL_ATTR_INIT_COMMAND] .= ', @@session.sql_mode = '.$sql;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ($this->compress === TRUE)\n\t\t{\n\t\t\t$this->options[PDO::MYSQL_ATTR_COMPRESS] = TRUE;\n\t\t}\n\n\t\tif (is_array($this->encrypt))\n\t\t{\n\t\t\t$ssl = array();\n\t\t\tempty($this->encrypt['ssl_key'])    OR $ssl[PDO::MYSQL_ATTR_SSL_KEY]    = $this->encrypt['ssl_key'];\n\t\t\tempty($this->encrypt['ssl_cert'])   OR $ssl[PDO::MYSQL_ATTR_SSL_CERT]   = $this->encrypt['ssl_cert'];\n\t\t\tempty($this->encrypt['ssl_ca'])     OR $ssl[PDO::MYSQL_ATTR_SSL_CA]     = $this->encrypt['ssl_ca'];\n\t\t\tempty($this->encrypt['ssl_capath']) OR $ssl[PDO::MYSQL_ATTR_SSL_CAPATH] = $this->encrypt['ssl_capath'];\n\t\t\tempty($this->encrypt['ssl_cipher']) OR $ssl[PDO::MYSQL_ATTR_SSL_CIPHER] = $this->encrypt['ssl_cipher'];\n\n\t\t\tif (defined('PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT') && isset($this->encrypt['ssl_verify']))\n\t\t\t{\n\t\t\t\t$ssl[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = $this->encrypt['ssl_verify'];\n\t\t\t}\n\n\t\t\t// DO NOT use array_merge() here!\n\t\t\t// It re-indexes numeric keys and the PDO_MYSQL_ATTR_SSL_* constants are integers.\n\t\t\tempty($ssl) OR $this->options += $ssl;\n\t\t}\n\n\t\t// Prior to version 5.7.3, MySQL silently downgrades to an unencrypted connection if SSL setup fails\n\t\tif (\n\t\t\t($pdo = parent::db_connect($persistent)) !== FALSE\n\t\t\t&& ! empty($ssl)\n\t\t\t&& version_compare($pdo->getAttribute(PDO::ATTR_CLIENT_VERSION), '5.7.3', '<=')\n\t\t\t&& empty($pdo->query(\"SHOW STATUS LIKE 'ssl_cipher'\")->fetchObject()->Value)\n\t\t)\n\t\t{\n\t\t\t$message = 'PDO_MYSQL was configured for an SSL connection, but got an unencrypted connection instead!';\n\t\t\tlog_message('error', $message);\n\t\t\treturn ($this->db_debug) ? $this->display_error($message, '', TRUE) : FALSE;\n\t\t}\n\n\t\treturn $pdo;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Select the database\n\t *\n\t * @param\tstring\t$database\n\t * @return\tbool\n\t */\n\tpublic function db_select($database = '')\n\t{\n\t\tif ($database === '')\n\t\t{\n\t\t\t$database = $this->database;\n\t\t}\n\n\t\tif (FALSE !== $this->simple_query('USE '.$this->escape_identifiers($database)))\n\t\t{\n\t\t\t$this->database = $database;\n\t\t\t$this->data_cache = array();\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Begin Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_begin()\n\t{\n\t\t$this->conn_id->setAttribute(PDO::ATTR_AUTOCOMMIT, FALSE);\n\t\treturn $this->conn_id->beginTransaction();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Commit Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_commit()\n\t{\n\t\tif ($this->conn_id->commit())\n\t\t{\n\t\t\t$this->conn_id->setAttribute(PDO::ATTR_AUTOCOMMIT, TRUE);\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Rollback Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_rollback()\n\t{\n\t\tif ($this->conn_id->rollBack())\n\t\t{\n\t\t\t$this->conn_id->setAttribute(PDO::ATTR_AUTOCOMMIT, TRUE);\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show table query\n\t *\n\t * Generates a platform-specific query string so that the table names can be fetched\n\t *\n\t * @param\tbool\t$prefix_limit\n\t * @return\tstring\n\t */\n\tprotected function _list_tables($prefix_limit = FALSE)\n\t{\n\t\t$sql = 'SHOW TABLES FROM '.$this->_escape_char.$this->database.$this->_escape_char;\n\n\t\tif ($prefix_limit === TRUE && $this->dbprefix !== '')\n\t\t{\n\t\t\treturn $sql.\" LIKE '\".$this->escape_like_str($this->dbprefix).\"%'\";\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show column query\n\t *\n\t * Generates a platform-specific query string so that the column names can be fetched\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _list_columns($table = '')\n\t{\n\t\treturn 'SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns an object with field data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tarray\n\t */\n\tpublic function field_data($table)\n\t{\n\t\tif (($query = $this->query('SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE))) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\t\t$query = $query->result_object();\n\n\t\t$retval = array();\n\t\tfor ($i = 0, $c = count($query); $i < $c; $i++)\n\t\t{\n\t\t\t$retval[$i]\t\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t\t= $query[$i]->Field;\n\n\t\t\tsscanf($query[$i]->Type, '%[a-z](%d)',\n\t\t\t\t$retval[$i]->type,\n\t\t\t\t$retval[$i]->max_length\n\t\t\t);\n\n\t\t\t$retval[$i]->default\t\t= $query[$i]->Default;\n\t\t\t$retval[$i]->primary_key\t= (int) ($query[$i]->Key === 'PRI');\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Truncate statement\n\t *\n\t * Generates a platform-specific truncate string from the supplied data\n\t *\n\t * If the database does not support the TRUNCATE statement,\n\t * then this method maps to 'DELETE FROM table'\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _truncate($table)\n\t{\n\t\treturn 'TRUNCATE '.$table;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * FROM tables\n\t *\n\t * Groups tables in FROM clauses if needed, so there is no confusion\n\t * about operator precedence.\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _from_tables()\n\t{\n\t\tif ( ! empty($this->qb_join) && count($this->qb_from) > 1)\n\t\t{\n\t\t\treturn '('.implode(', ', $this->qb_from).')';\n\t\t}\n\n\t\treturn implode(', ', $this->qb_from);\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_mysql_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO MySQL Forge Class\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_mysql_forge extends CI_DB_pdo_forge {\n\n\t/**\n\t * CREATE DATABASE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_database\t= 'CREATE DATABASE %s CHARACTER SET %s COLLATE %s';\n\n\t/**\n\t * CREATE TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_table_if\t= 'CREATE TABLE IF NOT EXISTS';\n\n\t/**\n\t * CREATE TABLE keys flag\n\t *\n\t * Whether table keys are created from within the\n\t * CREATE TABLE statement.\n\t *\n\t * @var\tbool\n\t */\n\tprotected $_create_table_keys\t= TRUE;\n\n\t/**\n\t * DROP TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_drop_table_if\t= 'DROP TABLE IF EXISTS';\n\n\t/**\n\t * UNSIGNED support\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_unsigned\t\t= array(\n\t\t'TINYINT',\n\t\t'SMALLINT',\n\t\t'MEDIUMINT',\n\t\t'INT',\n\t\t'INTEGER',\n\t\t'BIGINT',\n\t\t'REAL',\n\t\t'DOUBLE',\n\t\t'DOUBLE PRECISION',\n\t\t'FLOAT',\n\t\t'DECIMAL',\n\t\t'NUMERIC'\n\t);\n\n\t/**\n\t * NULL value representation in CREATE/ALTER TABLE statements\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_null = 'NULL';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * CREATE TABLE attributes\n\t *\n\t * @param\tarray\t$attributes\tAssociative array of table attributes\n\t * @return\tstring\n\t */\n\tprotected function _create_table_attr($attributes)\n\t{\n\t\t$sql = '';\n\n\t\tforeach (array_keys($attributes) as $key)\n\t\t{\n\t\t\tif (is_string($key))\n\t\t\t{\n\t\t\t\t$sql .= ' '.strtoupper($key).' = '.$attributes[$key];\n\t\t\t}\n\t\t}\n\n\t\tif ( ! empty($this->db->char_set) && ! strpos($sql, 'CHARACTER SET') && ! strpos($sql, 'CHARSET'))\n\t\t{\n\t\t\t$sql .= ' DEFAULT CHARACTER SET = '.$this->db->char_set;\n\t\t}\n\n\t\tif ( ! empty($this->db->dbcollat) && ! strpos($sql, 'COLLATE'))\n\t\t{\n\t\t\t$sql .= ' COLLATE = '.$this->db->dbcollat;\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ALTER TABLE\n\t *\n\t * @param\tstring\t$alter_type\tALTER type\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tmixed\t$field\t\tColumn definition\n\t * @return\tstring|string[]\n\t */\n\tprotected function _alter_table($alter_type, $table, $field)\n\t{\n\t\tif ($alter_type === 'DROP')\n\t\t{\n\t\t\treturn parent::_alter_table($alter_type, $table, $field);\n\t\t}\n\n\t\t$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table);\n\t\tfor ($i = 0, $c = count($field); $i < $c; $i++)\n\t\t{\n\t\t\tif ($field[$i]['_literal'] !== FALSE)\n\t\t\t{\n\t\t\t\t$field[$i] = ($alter_type === 'ADD')\n\t\t\t\t\t\t? \"\\n\\tADD \".$field[$i]['_literal']\n\t\t\t\t\t\t: \"\\n\\tMODIFY \".$field[$i]['_literal'];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif ($alter_type === 'ADD')\n\t\t\t\t{\n\t\t\t\t\t$field[$i]['_literal'] = \"\\n\\tADD \";\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$field[$i]['_literal'] = empty($field[$i]['new_name']) ? \"\\n\\tMODIFY \" : \"\\n\\tCHANGE \";\n\t\t\t\t}\n\n\t\t\t\t$field[$i] = $field[$i]['_literal'].$this->_process_column($field[$i]);\n\t\t\t}\n\t\t}\n\n\t\treturn array($sql.implode(',', $field));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Process column\n\t *\n\t * @param\tarray\t$field\n\t * @return\tstring\n\t */\n\tprotected function _process_column($field)\n\t{\n\t\t$extra_clause = isset($field['after'])\n\t\t\t? ' AFTER '.$this->db->escape_identifiers($field['after']) : '';\n\n\t\tif (empty($extra_clause) && isset($field['first']) && $field['first'] === TRUE)\n\t\t{\n\t\t\t$extra_clause = ' FIRST';\n\t\t}\n\n\t\treturn $this->db->escape_identifiers($field['name'])\n\t\t\t.(empty($field['new_name']) ? '' : ' '.$this->db->escape_identifiers($field['new_name']))\n\t\t\t.' '.$field['type'].$field['length']\n\t\t\t.$field['unsigned']\n\t\t\t.$field['null']\n\t\t\t.$field['default']\n\t\t\t.$field['auto_increment']\n\t\t\t.$field['unique']\n\t\t\t.(empty($field['comment']) ? '' : ' COMMENT '.$field['comment'])\n\t\t\t.$extra_clause;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Process indexes\n\t *\n\t * @param\tstring\t$table\t(ignored)\n\t * @return\tstring\n\t */\n\tprotected function _process_indexes($table)\n\t{\n\t\t$sql = '';\n\n\t\tfor ($i = 0, $c = count($this->keys); $i < $c; $i++)\n\t\t{\n\t\t\tif (is_array($this->keys[$i]))\n\t\t\t{\n\t\t\t\tfor ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++)\n\t\t\t\t{\n\t\t\t\t\tif ( ! isset($this->fields[$this->keys[$i][$i2]]))\n\t\t\t\t\t{\n\t\t\t\t\t\tunset($this->keys[$i][$i2]);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telseif ( ! isset($this->fields[$this->keys[$i]]))\n\t\t\t{\n\t\t\t\tunset($this->keys[$i]);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tis_array($this->keys[$i]) OR $this->keys[$i] = array($this->keys[$i]);\n\n\t\t\t$sql .= \",\\n\\tKEY \".$this->db->escape_identifiers(implode('_', $this->keys[$i]))\n\t\t\t\t.' ('.implode(', ', $this->db->escape_identifiers($this->keys[$i])).')';\n\t\t}\n\n\t\t$this->keys = array();\n\n\t\treturn $sql;\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_oci_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO Oracle Database Adapter Class\n *\n * Note: _DB is an extender class that the app controller\n * creates dynamically based on whether the query builder\n * class is being used or not.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_oci_driver extends CI_DB_pdo_driver {\n\n\t/**\n\t * Sub-driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $subdriver = 'oci';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * List of reserved identifiers\n\t *\n\t * Identifiers that must NOT be escaped.\n\t *\n\t * @var\tstring[]\n\t */\n\tprotected $_reserved_identifiers = array('*', 'rownum');\n\n\t/**\n\t * ORDER BY random keyword\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_random_keyword = array('ASC', 'ASC'); // Currently not supported\n\n\t/**\n\t * COUNT string\n\t *\n\t * @used-by\tCI_DB_driver::count_all()\n\t * @used-by\tCI_DB_query_builder::count_all_results()\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_count_string = 'SELECT COUNT(1) AS ';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * Builds the DSN if not already set.\n\t *\n\t * @param\tarray\t$params\n\t * @return\tvoid\n\t */\n\tpublic function __construct($params)\n\t{\n\t\tparent::__construct($params);\n\n\t\tif (empty($this->dsn))\n\t\t{\n\t\t\t$this->dsn = 'oci:dbname=';\n\n\t\t\t// Oracle has a slightly different PDO DSN format (Easy Connect),\n\t\t\t// which also supports pre-defined DSNs.\n\t\t\tif (empty($this->hostname) && empty($this->port))\n\t\t\t{\n\t\t\t\t$this->dsn .= $this->database;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$this->dsn .= '//'.(empty($this->hostname) ? '127.0.0.1' : $this->hostname)\n\t\t\t\t\t.(empty($this->port) ? '' : ':'.$this->port).'/';\n\n\t\t\t\tempty($this->database) OR $this->dsn .= $this->database;\n\t\t\t}\n\n\t\t\tempty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;\n\t\t}\n\t\telseif ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 4) === FALSE)\n\t\t{\n\t\t\t$this->dsn .= ';charset='.$this->char_set;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Database version number\n\t *\n\t * @return\tstring\n\t */\n\tpublic function version()\n\t{\n\t\tif (isset($this->data_cache['version']))\n\t\t{\n\t\t\treturn $this->data_cache['version'];\n\t\t}\n\n\t\t$version_string = parent::version();\n\t\tif (preg_match('#(Release\\s)?(?<version>\\d+(?:\\.\\d+)+)#', $version_string, $match))\n\t\t{\n\t\t\treturn $this->data_cache['version'] = $match['version'];\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show table query\n\t *\n\t * Generates a platform-specific query string so that the table names can be fetched\n\t *\n\t * @param\tbool\t$prefix_limit\n\t * @return\tstring\n\t */\n\tprotected function _list_tables($prefix_limit = FALSE)\n\t{\n\t\t$sql = 'SELECT \"TABLE_NAME\" FROM \"ALL_TABLES\"';\n\n\t\tif ($prefix_limit === TRUE && $this->dbprefix !== '')\n\t\t{\n\t\t\treturn $sql.' WHERE \"TABLE_NAME\" LIKE \\''.$this->escape_like_str($this->dbprefix).\"%' \"\n\t\t\t\t.sprintf($this->_like_escape_str, $this->_like_escape_chr);\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show column query\n\t *\n\t * Generates a platform-specific query string so that the column names can be fetched\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _list_columns($table = '')\n\t{\n\t\tif (strpos($table, '.') !== FALSE)\n\t\t{\n\t\t\tsscanf($table, '%[^.].%s', $owner, $table);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$owner = $this->username;\n\t\t}\n\n\t\treturn 'SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS\n\t\t\tWHERE UPPER(OWNER) = '.$this->escape(strtoupper($owner)).'\n\t\t\t\tAND UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns an object with field data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tarray\n\t */\n\tpublic function field_data($table)\n\t{\n\t\tif (strpos($table, '.') !== FALSE)\n\t\t{\n\t\t\tsscanf($table, '%[^.].%s', $owner, $table);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$owner = $this->username;\n\t\t}\n\n\t\t$sql = 'SELECT COLUMN_NAME, DATA_TYPE, CHAR_LENGTH, DATA_PRECISION, DATA_LENGTH, DATA_DEFAULT, NULLABLE\n\t\t\tFROM ALL_TAB_COLUMNS\n\t\t\tWHERE UPPER(OWNER) = '.$this->escape(strtoupper($owner)).'\n\t\t\t\tAND UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));\n\n\t\tif (($query = $this->query($sql)) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\t\t$query = $query->result_object();\n\n\t\t$retval = array();\n\t\tfor ($i = 0, $c = count($query); $i < $c; $i++)\n\t\t{\n\t\t\t$retval[$i]\t\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t\t= $query[$i]->COLUMN_NAME;\n\t\t\t$retval[$i]->type\t\t= $query[$i]->DATA_TYPE;\n\n\t\t\t$length = ($query[$i]->CHAR_LENGTH > 0)\n\t\t\t\t? $query[$i]->CHAR_LENGTH : $query[$i]->DATA_PRECISION;\n\t\t\tif ($length === NULL)\n\t\t\t{\n\t\t\t\t$length = $query[$i]->DATA_LENGTH;\n\t\t\t}\n\t\t\t$retval[$i]->max_length\t\t= $length;\n\n\t\t\t$default = $query[$i]->DATA_DEFAULT;\n\t\t\tif ($default === NULL && $query[$i]->NULLABLE === 'N')\n\t\t\t{\n\t\t\t\t$default = '';\n\t\t\t}\n\t\t\t$retval[$i]->default\t\t= $query[$i]->COLUMN_DEFAULT;\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert batch statement\n\t *\n\t * @param\tstring\t$table\tTable name\n\t * @param\tarray\t$keys\tINSERT keys\n\t * @param\tarray\t$values\tINSERT values\n\t * @return \tstring\n\t */\n\tprotected function _insert_batch($table, $keys, $values)\n\t{\n\t\t$keys = implode(', ', $keys);\n\t\t$sql = \"INSERT ALL\\n\";\n\n\t\tfor ($i = 0, $c = count($values); $i < $c; $i++)\n\t\t{\n\t\t\t$sql .= '\tINTO '.$table.' ('.$keys.') VALUES '.$values[$i].\"\\n\";\n\t\t}\n\n\t\treturn $sql.'SELECT * FROM dual';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Delete statement\n\t *\n\t * Generates a platform-specific delete string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _delete($table)\n\t{\n\t\tif ($this->qb_limit)\n\t\t{\n\t\t\t$this->where('rownum <= ',$this->qb_limit, FALSE);\n\t\t\t$this->qb_limit = FALSE;\n\t\t}\n\n\t\treturn parent::_delete($table);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * LIMIT\n\t *\n\t * Generates a platform-specific LIMIT clause\n\t *\n\t * @param\tstring\t$sql\tSQL Query\n\t * @return\tstring\n\t */\n\tprotected function _limit($sql)\n\t{\n\t\tif (version_compare($this->version(), '12.1', '>='))\n\t\t{\n\t\t\t// OFFSET-FETCH can be used only with the ORDER BY clause\n\t\t\tempty($this->qb_orderby) && $sql .= ' ORDER BY 1';\n\n\t\t\treturn $sql.' OFFSET '.(int) $this->qb_offset.' ROWS FETCH NEXT '.$this->qb_limit.' ROWS ONLY';\n\t\t}\n\n\t\treturn 'SELECT * FROM (SELECT inner_query.*, rownum rnum FROM ('.$sql.') inner_query WHERE rownum < '.($this->qb_offset + $this->qb_limit + 1).')'\n\t\t\t.($this->qb_offset ? ' WHERE rnum >= '.($this->qb_offset + 1): '');\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_oci_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO Oracle Forge Class\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_oci_forge extends CI_DB_pdo_forge {\n\n\t/**\n\t * CREATE DATABASE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_database\t= FALSE;\n\n\t/**\n\t * CREATE TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_table_if\t= FALSE;\n\n\t/**\n\t * DROP DATABASE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_drop_database\t= FALSE;\n\n\t/**\n\t * UNSIGNED support\n\t *\n\t * @var\tbool|array\n\t */\n\tprotected $_unsigned\t\t= FALSE;\n\n\t/**\n\t * NULL value representation in CREATE/ALTER TABLE statements\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_null\t\t= 'NULL';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ALTER TABLE\n\t *\n\t * @param\tstring\t$alter_type\tALTER type\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tmixed\t$field\t\tColumn definition\n\t * @return\tstring|string[]\n\t */\n\tprotected function _alter_table($alter_type, $table, $field)\n\t{\n\t\tif ($alter_type === 'DROP')\n\t\t{\n\t\t\treturn parent::_alter_table($alter_type, $table, $field);\n\t\t}\n\t\telseif ($alter_type === 'CHANGE')\n\t\t{\n\t\t\t$alter_type = 'MODIFY';\n\t\t}\n\n\t\t$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table);\n\t\t$sqls = array();\n\t\tfor ($i = 0, $c = count($field); $i < $c; $i++)\n\t\t{\n\t\t\tif ($field[$i]['_literal'] !== FALSE)\n\t\t\t{\n\t\t\t\t$field[$i] = \"\\n\\t\".$field[$i]['_literal'];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$field[$i]['_literal'] = \"\\n\\t\".$this->_process_column($field[$i]);\n\n\t\t\t\tif ( ! empty($field[$i]['comment']))\n\t\t\t\t{\n\t\t\t\t\t$sqls[] = 'COMMENT ON COLUMN '\n\t\t\t\t\t\t.$this->db->escape_identifiers($table).'.'.$this->db->escape_identifiers($field[$i]['name'])\n\t\t\t\t\t\t.' IS '.$field[$i]['comment'];\n\t\t\t\t}\n\n\t\t\t\tif ($alter_type === 'MODIFY' && ! empty($field[$i]['new_name']))\n\t\t\t\t{\n\t\t\t\t\t$sqls[] = $sql.' RENAME COLUMN '.$this->db->escape_identifiers($field[$i]['name'])\n\t\t\t\t\t\t.' TO '.$this->db->escape_identifiers($field[$i]['new_name']);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$sql .= ' '.$alter_type.' ';\n\t\t$sql .= (count($field) === 1)\n\t\t\t\t? $field[0]\n\t\t\t\t: '('.implode(',', $field).')';\n\n\t\t// RENAME COLUMN must be executed after MODIFY\n\t\tarray_unshift($sqls, $sql);\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute AUTO_INCREMENT\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_auto_increment(&$attributes, &$field)\n\t{\n\t\tif ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'number') !== FALSE && version_compare($this->db->version(), '12.1', '>='))\n\t\t{\n\t\t\t$field['auto_increment'] = ' GENERATED ALWAYS AS IDENTITY';\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Process column\n\t *\n\t * @param\tarray\t$field\n\t * @return\tstring\n\t */\n\tprotected function _process_column($field)\n\t{\n\t\treturn $this->db->escape_identifiers($field['name'])\n\t\t\t.' '.$field['type'].$field['length']\n\t\t\t.$field['unsigned']\n\t\t\t.$field['default']\n\t\t\t.$field['auto_increment']\n\t\t\t.$field['null']\n\t\t\t.$field['unique'];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute TYPE\n\t *\n\t * Performs a data type mapping between different databases.\n\t *\n\t * @param\tarray\t&$attributes\n\t * @return\tvoid\n\t */\n\tprotected function _attr_type(&$attributes)\n\t{\n\t\tswitch (strtoupper($attributes['TYPE']))\n\t\t{\n\t\t\tcase 'TINYINT':\n\t\t\t\t$attributes['TYPE'] = 'NUMBER';\n\t\t\t\treturn;\n\t\t\tcase 'MEDIUMINT':\n\t\t\t\t$attributes['TYPE'] = 'NUMBER';\n\t\t\t\treturn;\n\t\t\tcase 'INT':\n\t\t\t\t$attributes['TYPE'] = 'NUMBER';\n\t\t\t\treturn;\n\t\t\tcase 'BIGINT':\n\t\t\t\t$attributes['TYPE'] = 'NUMBER';\n\t\t\t\treturn;\n\t\t\tdefault: return;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO ODBC Database Adapter Class\n *\n * Note: _DB is an extender class that the app controller\n * creates dynamically based on whether the query builder\n * class is being used or not.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_odbc_driver extends CI_DB_pdo_driver {\n\n\t/**\n\t * Sub-driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $subdriver = 'odbc';\n\n\t/**\n\t * Database schema\n\t *\n\t * @var\tstring\n\t */\n\tpublic $schema = 'public';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Identifier escape character\n\t *\n\t * Must be empty for ODBC.\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_escape_char = '';\n\n\t/**\n\t * ESCAPE statement string\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_like_escape_str = \" {escape '%s'} \";\n\n\t/**\n\t * ORDER BY random keyword\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_random_keyword = array('RND()', 'RND(%d)');\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * Builds the DSN if not already set.\n\t *\n\t * @param\tarray\t$params\n\t * @return\tvoid\n\t */\n\tpublic function __construct($params)\n\t{\n\t\tparent::__construct($params);\n\n\t\tif (empty($this->dsn))\n\t\t{\n\t\t\t$this->dsn = 'odbc:';\n\n\t\t\t// Pre-defined DSN\n\t\t\tif (empty($this->hostname) && empty($this->HOSTNAME) && empty($this->port) && empty($this->PORT))\n\t\t\t{\n\t\t\t\tif (isset($this->DSN))\n\t\t\t\t{\n\t\t\t\t\t$this->dsn .= 'DSN='.$this->DSN;\n\t\t\t\t}\n\t\t\t\telseif ( ! empty($this->database))\n\t\t\t\t{\n\t\t\t\t\t$this->dsn .= 'DSN='.$this->database;\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// If the DSN is not pre-configured - try to build an IBM DB2 connection string\n\t\t\t$this->dsn .= 'DRIVER='.(isset($this->DRIVER) ? '{'.$this->DRIVER.'}' : '{IBM DB2 ODBC DRIVER}').';';\n\n\t\t\tif (isset($this->DATABASE))\n\t\t\t{\n\t\t\t\t$this->dsn .= 'DATABASE='.$this->DATABASE.';';\n\t\t\t}\n\t\t\telseif ( ! empty($this->database))\n\t\t\t{\n\t\t\t\t$this->dsn .= 'DATABASE='.$this->database.';';\n\t\t\t}\n\n\t\t\tif (isset($this->HOSTNAME))\n\t\t\t{\n\t\t\t\t$this->dsn .= 'HOSTNAME='.$this->HOSTNAME.';';\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$this->dsn .= 'HOSTNAME='.(empty($this->hostname) ? '127.0.0.1;' : $this->hostname.';');\n\t\t\t}\n\n\t\t\tif (isset($this->PORT))\n\t\t\t{\n\t\t\t\t$this->dsn .= 'PORT='.$this->port.';';\n\t\t\t}\n\t\t\telseif ( ! empty($this->port))\n\t\t\t{\n\t\t\t\t$this->dsn .= ';PORT='.$this->port.';';\n\t\t\t}\n\n\t\t\t$this->dsn .= 'PROTOCOL='.(isset($this->PROTOCOL) ? $this->PROTOCOL.';' : 'TCPIP;');\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Platform-dependent string escape\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tprotected function _escape_str($str)\n\t{\n\t\t$this->display_error('db_unsupported_feature');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Determines if a query is a \"write\" type.\n\t *\n\t * @param\tstring\tAn SQL query string\n\t * @return\tbool\n\t */\n\tpublic function is_write_type($sql)\n\t{\n\t\tif (preg_match('#^(INSERT|UPDATE).*RETURNING\\s.+(\\,\\s?.+)*$#is', $sql))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn parent::is_write_type($sql);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show table query\n\t *\n\t * Generates a platform-specific query string so that the table names can be fetched\n\t *\n\t * @param\tbool\t$prefix_limit\n\t * @return\tstring\n\t */\n\tprotected function _list_tables($prefix_limit = FALSE)\n\t{\n\t\t$sql = \"SELECT table_name FROM information_schema.tables WHERE table_schema = '\".$this->schema.\"'\";\n\n\t\tif ($prefix_limit !== FALSE && $this->dbprefix !== '')\n\t\t{\n\t\t\treturn $sql.\" AND table_name LIKE '\".$this->escape_like_str($this->dbprefix).\"%' \"\n\t\t\t\t.sprintf($this->_like_escape_str, $this->_like_escape_chr);\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show column query\n\t *\n\t * Generates a platform-specific query string so that the column names can be fetched\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _list_columns($table = '')\n\t{\n\t\treturn 'SELECT column_name FROM information_schema.columns WHERE table_name = '.$this->escape($table);\n\t}\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_odbc_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO ODBC Forge Class\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/database/\n */\nclass CI_DB_pdo_odbc_forge extends CI_DB_pdo_forge {\n\n\t/**\n\t * UNSIGNED support\n\t *\n\t * @var\tbool|array\n\t */\n\tprotected $_unsigned\t\t= FALSE;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute AUTO_INCREMENT\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_auto_increment(&$attributes, &$field)\n\t{\n\t\t// Not supported (in most databases at least)\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO PostgreSQL Database Adapter Class\n *\n * Note: _DB is an extender class that the app controller\n * creates dynamically based on whether the query builder\n * class is being used or not.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_pgsql_driver extends CI_DB_pdo_driver {\n\n\t/**\n\t * Sub-driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $subdriver = 'pgsql';\n\n\t/**\n\t * Database schema\n\t *\n\t * @var\tstring\n\t */\n\tpublic $schema = 'public';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ORDER BY random keyword\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_random_keyword = array('RANDOM()', 'RANDOM()');\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * Builds the DSN if not already set.\n\t *\n\t * @param\tarray\t$params\n\t * @return\tvoid\n\t */\n\tpublic function __construct($params)\n\t{\n\t\tparent::__construct($params);\n\n\t\tif (empty($this->dsn))\n\t\t{\n\t\t\t$this->dsn = 'pgsql:host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);\n\n\t\t\tempty($this->port) OR $this->dsn .= ';port='.$this->port;\n\t\t\tempty($this->database) OR $this->dsn .= ';dbname='.$this->database;\n\n\t\t\tif ( ! empty($this->username))\n\t\t\t{\n\t\t\t\t$this->dsn .= ';user='.$this->username;\n\t\t\t\tempty($this->password) OR $this->dsn .= ';password='.$this->password;\n\t\t\t}\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Database connection\n\t *\n\t * @param\tbool\t$persistent\n\t * @return\tobject\n\t */\n\tpublic function db_connect($persistent = FALSE)\n\t{\n\t\t$this->conn_id = parent::db_connect($persistent);\n\n\t\tif (is_object($this->conn_id) && ! empty($this->schema))\n\t\t{\n\t\t\t$this->simple_query('SET search_path TO '.$this->schema.',public');\n\t\t}\n\n\t\treturn $this->conn_id;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert ID\n\t *\n\t * @param\tstring\t$name\n\t * @return\tint\n\t */\n\tpublic function insert_id($name = NULL)\n\t{\n\t\tif ($name === NULL && version_compare($this->version(), '8.1', '>='))\n\t\t{\n\t\t\t$query = $this->query('SELECT LASTVAL() AS ins_id');\n\t\t\t$query = $query->row();\n\t\t\treturn $query->ins_id;\n\t\t}\n\n\t\treturn $this->conn_id->lastInsertId($name);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Determines if a query is a \"write\" type.\n\t *\n\t * @param\tstring\tAn SQL query string\n\t * @return\tbool\n\t */\n\tpublic function is_write_type($sql)\n\t{\n\t\tif (preg_match('#^(INSERT|UPDATE).*RETURNING\\s.+(\\,\\s?.+)*$#is', $sql))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn parent::is_write_type($sql);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * \"Smart\" Escape String\n\t *\n\t * Escapes data based on type\n\t *\n\t * @param\tstring\t$str\n\t * @return\tmixed\n\t */\n\tpublic function escape($str)\n\t{\n\t\tif (is_bool($str))\n\t\t{\n\t\t\treturn ($str) ? 'TRUE' : 'FALSE';\n\t\t}\n\n\t\treturn parent::escape($str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ORDER BY\n\t *\n\t * @param\tstring\t$orderby\n\t * @param\tstring\t$direction\tASC, DESC or RANDOM\n\t * @param\tbool\t$escape\n\t * @return\tobject\n\t */\n\tpublic function order_by($orderby, $direction = '', $escape = NULL)\n\t{\n\t\t$direction = strtoupper(trim($direction));\n\t\tif ($direction === 'RANDOM')\n\t\t{\n\t\t\tif ( ! is_float($orderby) && ctype_digit((string) $orderby))\n\t\t\t{\n\t\t\t\t$orderby = ($orderby > 1)\n\t\t\t\t\t? (float) '0.'.$orderby\n\t\t\t\t\t: (float) $orderby;\n\t\t\t}\n\n\t\t\tif (is_float($orderby))\n\t\t\t{\n\t\t\t\t$this->simple_query('SET SEED '.$orderby);\n\t\t\t}\n\n\t\t\t$orderby = $this->_random_keyword[0];\n\t\t\t$direction = '';\n\t\t\t$escape = FALSE;\n\t\t}\n\n\t\treturn parent::order_by($orderby, $direction, $escape);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show table query\n\t *\n\t * Generates a platform-specific query string so that the table names can be fetched\n\t *\n\t * @param\tbool\t$prefix_limit\n\t * @return\tstring\n\t */\n\tprotected function _list_tables($prefix_limit = FALSE)\n\t{\n\t\t$sql = 'SELECT \"table_name\" FROM \"information_schema\".\"tables\" WHERE \"table_schema\" = \\''.$this->schema.\"'\";\n\n\t\tif ($prefix_limit === TRUE && $this->dbprefix !== '')\n\t\t{\n\t\t\treturn $sql.' AND \"table_name\" LIKE \\''\n\t\t\t\t.$this->escape_like_str($this->dbprefix).\"%' \"\n\t\t\t\t.sprintf($this->_like_escape_str, $this->_like_escape_chr);\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * List column query\n\t *\n\t * Generates a platform-specific query string so that the column names can be fetched\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _list_columns($table = '')\n\t{\n\t\treturn 'SELECT \"column_name\"\n\t\t\tFROM \"information_schema\".\"columns\"\n\t\t\tWHERE \"table_schema\" = \\''.$this->schema.'\\' AND LOWER(\"table_name\") = '.$this->escape(strtolower($table));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns an object with field data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tarray\n\t */\n\tpublic function field_data($table)\n\t{\n\t\t$sql = 'SELECT \"column_name\", \"data_type\", \"character_maximum_length\", \"numeric_precision\", \"column_default\"\n\t\t\tFROM \"information_schema\".\"columns\"\n\t\t\tWHERE \"table_schema\" = \\''.$this->schema.'\\' AND LOWER(\"table_name\") = '.$this->escape(strtolower($table));\n\n\t\tif (($query = $this->query($sql)) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\t\t$query = $query->result_object();\n\n\t\t$retval = array();\n\t\tfor ($i = 0, $c = count($query); $i < $c; $i++)\n\t\t{\n\t\t\t$retval[$i]\t\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t\t= $query[$i]->column_name;\n\t\t\t$retval[$i]->type\t\t= $query[$i]->data_type;\n\t\t\t$retval[$i]->max_length\t\t= ($query[$i]->character_maximum_length > 0) ? $query[$i]->character_maximum_length : $query[$i]->numeric_precision;\n\t\t\t$retval[$i]->default\t\t= $query[$i]->column_default;\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Update statement\n\t *\n\t * Generates a platform-specific update string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @param\tarray\t$values\n\t * @return\tstring\n\t */\n\tprotected function _update($table, $values)\n\t{\n\t\t$this->qb_limit = FALSE;\n\t\t$this->qb_orderby = array();\n\t\treturn parent::_update($table, $values);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Update_Batch statement\n\t *\n\t * Generates a platform-specific batch update string from the supplied data\n\t *\n\t * @param\tstring\t$table\tTable name\n\t * @param\tarray\t$values\tUpdate data\n\t * @param\tstring\t$index\tWHERE key\n\t * @return\tstring\n\t */\n\tprotected function _update_batch($table, $values, $index)\n\t{\n\t\t$ids = array();\n\t\tforeach ($values as $key => $val)\n\t\t{\n\t\t\t$ids[] = $val[$index]['value'];\n\n\t\t\tforeach (array_keys($val) as $field)\n\t\t\t{\n\t\t\t\tif ($field !== $index)\n\t\t\t\t{\n\t\t\t\t\t$final[$val[$field]['field']][] = 'WHEN '.$val[$index]['value'].' THEN '.$val[$field]['value'];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$cases = '';\n\t\tforeach ($final as $k => $v)\n\t\t{\n\t\t\t$cases .= $k.' = (CASE '.$val[$index]['field'].\"\\n\"\n\t\t\t\t.implode(\"\\n\", $v).\"\\n\"\n\t\t\t\t.'ELSE '.$k.' END), ';\n\t\t}\n\n\t\t$this->where($val[$index]['field'].' IN('.implode(',', $ids).')', NULL, FALSE);\n\n\t\treturn 'UPDATE '.$table.' SET '.substr($cases, 0, -2).$this->_compile_wh('qb_where');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Delete statement\n\t *\n\t * Generates a platform-specific delete string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _delete($table)\n\t{\n\t\t$this->qb_limit = FALSE;\n\t\treturn parent::_delete($table);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * LIMIT\n\t *\n\t * Generates a platform-specific LIMIT clause\n\t *\n\t * @param\tstring\t$sql\tSQL Query\n\t * @return\tstring\n\t */\n\tprotected function _limit($sql)\n\t{\n\t\treturn $sql.' LIMIT '.$this->qb_limit.($this->qb_offset ? ' OFFSET '.$this->qb_offset : '');\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_pgsql_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO PostgreSQL Forge Class\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_pgsql_forge extends CI_DB_pdo_forge {\n\n\t/**\n\t * DROP TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_drop_table_if\t= 'DROP TABLE IF EXISTS';\n\n\t/**\n\t * CREATE TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_table_if\t= 'CREATE TABLE IF NOT EXISTS';\n\n\t/**\n\t * UNSIGNED support\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_unsigned\t\t= array(\n\t\t'INT2'\t\t=> 'INTEGER',\n\t\t'SMALLINT'\t=> 'INTEGER',\n\t\t'INT'\t\t=> 'BIGINT',\n\t\t'INT4'\t\t=> 'BIGINT',\n\t\t'INTEGER'\t=> 'BIGINT',\n\t\t'INT8'\t\t=> 'NUMERIC',\n\t\t'BIGINT'\t=> 'NUMERIC',\n\t\t'REAL'\t\t=> 'DOUBLE PRECISION',\n\t\t'FLOAT'\t\t=> 'DOUBLE PRECISION'\n\t);\n\n\t/**\n\t * NULL value representation in CREATE/ALTER TABLE statements\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_null = 'NULL';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * @param\tobject\t&$db\tDatabase object\n\t * @return\tvoid\n\t */\n\tpublic function __construct(&$db)\n\t{\n\t\tparent::__construct($db);\n\n\t\tif (version_compare($this->db->version(), '9.0', '>'))\n\t\t{\n\t\t\t$this->create_table_if = 'CREATE TABLE IF NOT EXISTS';\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ALTER TABLE\n\t *\n\t * @param\tstring\t$alter_type\tALTER type\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tmixed\t$field\t\tColumn definition\n\t * @return\tstring|string[]\n\t */\n\tprotected function _alter_table($alter_type, $table, $field)\n\t{\n\t\tif (in_array($alter_type, array('DROP', 'ADD'), TRUE))\n\t\t{\n\t\t\treturn parent::_alter_table($alter_type, $table, $field);\n\t\t}\n\n\t\t$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table);\n\t\t$sqls = array();\n\t\tfor ($i = 0, $c = count($field); $i < $c; $i++)\n\t\t{\n\t\t\tif ($field[$i]['_literal'] !== FALSE)\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\tif (version_compare($this->db->version(), '8', '>=') && isset($field[$i]['type']))\n\t\t\t{\n\t\t\t\t$sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identifiers($field[$i]['name'])\n\t\t\t\t\t.' TYPE '.$field[$i]['type'].$field[$i]['length'];\n\t\t\t}\n\n\t\t\tif ( ! empty($field[$i]['default']))\n\t\t\t{\n\t\t\t\t$sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identifiers($field[$i]['name'])\n\t\t\t\t\t.' SET '.$field[$i]['default'];\n\t\t\t}\n\n\t\t\tif (isset($field[$i]['null']))\n\t\t\t{\n\t\t\t\t$sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identifiers($field[$i]['name'])\n\t\t\t\t\t.(trim($field[$i]['null']) === $this->_null ? ' DROP NOT NULL' : ' SET NOT NULL');\n\t\t\t}\n\n\t\t\tif ( ! empty($field[$i]['new_name']))\n\t\t\t{\n\t\t\t\t$sqls[] = $sql.' RENAME COLUMN '.$this->db->escape_identifiers($field[$i]['name'])\n\t\t\t\t\t.' TO '.$this->db->escape_identifiers($field[$i]['new_name']);\n\t\t\t}\n\n\t\t\tif ( ! empty($field[$i]['comment']))\n\t\t\t{\n\t\t\t\t$sqls[] = 'COMMENT ON COLUMN '\n\t\t\t\t\t.$this->db->escape_identifiers($table).'.'.$this->db->escape_identifiers($field[$i]['name'])\n\t\t\t\t\t.' IS '.$field[$i]['comment'];\n\t\t\t}\n\t\t}\n\n\t\treturn $sqls;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute TYPE\n\t *\n\t * Performs a data type mapping between different databases.\n\t *\n\t * @param\tarray\t&$attributes\n\t * @return\tvoid\n\t */\n\tprotected function _attr_type(&$attributes)\n\t{\n\t\t// Reset field lengths for data types that don't support it\n\t\tif (isset($attributes['CONSTRAINT']) && stripos($attributes['TYPE'], 'int') !== FALSE)\n\t\t{\n\t\t\t$attributes['CONSTRAINT'] = NULL;\n\t\t}\n\n\t\tswitch (strtoupper($attributes['TYPE']))\n\t\t{\n\t\t\tcase 'TINYINT':\n\t\t\t\t$attributes['TYPE'] = 'SMALLINT';\n\t\t\t\t$attributes['UNSIGNED'] = FALSE;\n\t\t\t\treturn;\n\t\t\tcase 'MEDIUMINT':\n\t\t\t\t$attributes['TYPE'] = 'INTEGER';\n\t\t\t\t$attributes['UNSIGNED'] = FALSE;\n\t\t\t\treturn;\n\t\t\tdefault: return;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute AUTO_INCREMENT\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_auto_increment(&$attributes, &$field)\n\t{\n\t\tif ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE)\n\t\t{\n\t\t\t$field['type'] = ($field['type'] === 'NUMERIC')\n\t\t\t\t? 'BIGSERIAL'\n\t\t\t\t: 'SERIAL';\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO SQLite Database Adapter Class\n *\n * Note: _DB is an extender class that the app controller\n * creates dynamically based on whether the query builder\n * class is being used or not.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_sqlite_driver extends CI_DB_pdo_driver {\n\n\t/**\n\t * Sub-driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $subdriver = 'sqlite';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ORDER BY random keyword\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_random_keyword = array('RANDOM()', 'RANDOM()');\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * Builds the DSN if not already set.\n\t *\n\t * @param\tarray\t$params\n\t * @return\tvoid\n\t */\n\tpublic function __construct($params)\n\t{\n\t\tparent::__construct($params);\n\n\t\tif (empty($this->dsn))\n\t\t{\n\t\t\t$this->dsn = 'sqlite:';\n\n\t\t\tif (empty($this->database) && empty($this->hostname))\n\t\t\t{\n\t\t\t\t$this->database = ':memory:';\n\t\t\t}\n\n\t\t\t$this->database = empty($this->database) ? $this->hostname : $this->database;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show table query\n\t *\n\t * Generates a platform-specific query string so that the table names can be fetched\n\t *\n\t * @param\tbool\t$prefix_limit\n\t * @return\tstring\n\t */\n\tprotected function _list_tables($prefix_limit = FALSE)\n\t{\n\t\t$sql = 'SELECT \"NAME\" FROM \"SQLITE_MASTER\" WHERE \"TYPE\" = \\'table\\'';\n\n\t\tif ($prefix_limit === TRUE && $this->dbprefix !== '')\n\t\t{\n\t\t\treturn $sql.' AND \"NAME\" LIKE \\''.$this->escape_like_str($this->dbprefix).\"%' \"\n\t\t\t\t.sprintf($this->_like_escape_str, $this->_like_escape_chr);\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch Field Names\n\t *\n\t * @param\tstring\t$table\tTable name\n\t * @return\tarray\n\t */\n\tpublic function list_fields($table)\n\t{\n\t\tif (($result = $this->query('PRAGMA TABLE_INFO('.$this->protect_identifiers($table, TRUE, NULL, FALSE).')')) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$fields = array();\n\t\tforeach ($result->result_array() as $row)\n\t\t{\n\t\t\t$fields[] = $row['name'];\n\t\t}\n\n\t\treturn $fields;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns an object with field data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tarray\n\t */\n\tpublic function field_data($table)\n\t{\n\t\tif (($query = $this->query('PRAGMA TABLE_INFO('.$this->protect_identifiers($table, TRUE, NULL, FALSE).')')) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$query = $query->result_array();\n\t\tif (empty($query))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$retval = array();\n\t\tfor ($i = 0, $c = count($query); $i < $c; $i++)\n\t\t{\n\t\t\t$retval[$i]\t\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t\t= $query[$i]['name'];\n\t\t\t$retval[$i]->type\t\t= $query[$i]['type'];\n\t\t\t$retval[$i]->max_length\t\t= NULL;\n\t\t\t$retval[$i]->default\t\t= $query[$i]['dflt_value'];\n\t\t\t$retval[$i]->primary_key\t= isset($query[$i]['pk']) ? (int) $query[$i]['pk'] : 0;\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Replace statement\n\t *\n\t * @param\tstring\t$table\tTable name\n\t * @param\tarray\t$keys\tINSERT keys\n\t * @param\tarray\t$values\tINSERT values\n\t * @return \tstring\n\t */\n\tprotected function _replace($table, $keys, $values)\n\t{\n\t\treturn 'INSERT OR '.parent::_replace($table, $keys, $values);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Truncate statement\n\t *\n\t * Generates a platform-specific truncate string from the supplied data\n\t *\n\t * If the database does not support the TRUNCATE statement,\n\t * then this method maps to 'DELETE FROM table'\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _truncate($table)\n\t{\n\t\treturn 'DELETE FROM '.$table;\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO SQLite Forge Class\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_sqlite_forge extends CI_DB_pdo_forge {\n\n\t/**\n\t * CREATE TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_table_if\t= 'CREATE TABLE IF NOT EXISTS';\n\n\t/**\n\t * DROP TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_drop_table_if\t= 'DROP TABLE IF EXISTS';\n\n\t/**\n\t * UNSIGNED support\n\t *\n\t * @var\tbool|array\n\t */\n\tprotected $_unsigned\t\t= FALSE;\n\n\t/**\n\t * NULL value representation in CREATE/ALTER TABLE statements\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_null\t\t= 'NULL';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * @param\tobject\t&$db\tDatabase object\n\t * @return\tvoid\n\t */\n\tpublic function __construct(&$db)\n\t{\n\t\tparent::__construct($db);\n\n\t\tif (version_compare($this->db->version(), '3.3', '<'))\n\t\t{\n\t\t\t$this->_create_table_if = FALSE;\n\t\t\t$this->_drop_table_if   = FALSE;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Create database\n\t *\n\t * @param\tstring\t$db_name\t(ignored)\n\t * @return\tbool\n\t */\n\tpublic function create_database($db_name)\n\t{\n\t\t// In SQLite, a database is created when you connect to the database.\n\t\t// We'll return TRUE so that an error isn't generated\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Drop database\n\t *\n\t * @param\tstring\t$db_name\t(ignored)\n\t * @return\tbool\n\t */\n\tpublic function drop_database($db_name)\n\t{\n\t\t// In SQLite, a database is dropped when we delete a file\n\t\tif (file_exists($this->db->database))\n\t\t{\n\t\t\t// We need to close the pseudo-connection first\n\t\t\t$this->db->close();\n\t\t\tif ( ! @unlink($this->db->database))\n\t\t\t{\n\t\t\t\treturn $this->db->db_debug ? $this->db->display_error('db_unable_to_drop') : FALSE;\n\t\t\t}\n\t\t\telseif ( ! empty($this->db->data_cache['db_names']))\n\t\t\t{\n\t\t\t\t$key = array_search(strtolower($this->db->database), array_map('strtolower', $this->db->data_cache['db_names']), TRUE);\n\t\t\t\tif ($key !== FALSE)\n\t\t\t\t{\n\t\t\t\t\tunset($this->db->data_cache['db_names'][$key]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn $this->db->db_debug ? $this->db->display_error('db_unable_to_drop') : FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ALTER TABLE\n\t *\n\t * @param\tstring\t$alter_type\tALTER type\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tmixed\t$field\t\tColumn definition\n\t * @return\tstring|string[]\n\t */\n\tprotected function _alter_table($alter_type, $table, $field)\n\t{\n\t\tif ($alter_type === 'DROP' OR $alter_type === 'CHANGE')\n\t\t{\n\t\t\t// drop_column():\n\t\t\t//\tBEGIN TRANSACTION;\n\t\t\t//\tCREATE TEMPORARY TABLE t1_backup(a,b);\n\t\t\t//\tINSERT INTO t1_backup SELECT a,b FROM t1;\n\t\t\t//\tDROP TABLE t1;\n\t\t\t//\tCREATE TABLE t1(a,b);\n\t\t\t//\tINSERT INTO t1 SELECT a,b FROM t1_backup;\n\t\t\t//\tDROP TABLE t1_backup;\n\t\t\t//\tCOMMIT;\n\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn parent::_alter_table($alter_type, $table, $field);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Process column\n\t *\n\t * @param\tarray\t$field\n\t * @return\tstring\n\t */\n\tprotected function _process_column($field)\n\t{\n\t\treturn $this->db->escape_identifiers($field['name'])\n\t\t\t.' '.$field['type']\n\t\t\t.$field['auto_increment']\n\t\t\t.$field['null']\n\t\t\t.$field['unique']\n\t\t\t.$field['default'];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute TYPE\n\t *\n\t * Performs a data type mapping between different databases.\n\t *\n\t * @param\tarray\t&$attributes\n\t * @return\tvoid\n\t */\n\tprotected function _attr_type(&$attributes)\n\t{\n\t\tswitch (strtoupper($attributes['TYPE']))\n\t\t{\n\t\t\tcase 'ENUM':\n\t\t\tcase 'SET':\n\t\t\t\t$attributes['TYPE'] = 'TEXT';\n\t\t\t\treturn;\n\t\t\tdefault: return;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute AUTO_INCREMENT\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_auto_increment(&$attributes, &$field)\n\t{\n\t\tif ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE)\n\t\t{\n\t\t\t$field['type'] = 'INTEGER PRIMARY KEY';\n\t\t\t$field['default'] = '';\n\t\t\t$field['null'] = '';\n\t\t\t$field['unique'] = '';\n\t\t\t$field['auto_increment'] = ' AUTOINCREMENT';\n\n\t\t\t$this->primary_keys = array();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO SQLSRV Database Adapter Class\n *\n * Note: _DB is an extender class that the app controller\n * creates dynamically based on whether the query builder\n * class is being used or not.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_sqlsrv_driver extends CI_DB_pdo_driver {\n\n\t/**\n\t * Sub-driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $subdriver = 'sqlsrv';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ORDER BY random keyword\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_random_keyword = array('NEWID()', 'RAND(%d)');\n\n\t/**\n\t * Quoted identifier flag\n\t *\n\t * Whether to use SQL-92 standard quoted identifier\n\t * (double quotes) or brackets for identifier escaping.\n\t *\n\t * @var\tbool\n\t */\n\tprotected $_quoted_identifier;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * Builds the DSN if not already set.\n\t *\n\t * @param\tarray\t$params\n\t * @return\tvoid\n\t */\n\tpublic function __construct($params)\n\t{\n\t\tparent::__construct($params);\n\n\t\tif (empty($this->dsn))\n\t\t{\n\t\t\t$this->dsn = 'sqlsrv:Server='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);\n\n\t\t\tempty($this->port) OR $this->dsn .= ','.$this->port;\n\t\t\tempty($this->database) OR $this->dsn .= ';Database='.$this->database;\n\n\t\t\t// Some custom options\n\n\t\t\tif (isset($this->QuotedId))\n\t\t\t{\n\t\t\t\t$this->dsn .= ';QuotedId='.$this->QuotedId;\n\t\t\t\t$this->_quoted_identifier = (bool) $this->QuotedId;\n\t\t\t}\n\n\t\t\tif (isset($this->ConnectionPooling))\n\t\t\t{\n\t\t\t\t$this->dsn .= ';ConnectionPooling='.$this->ConnectionPooling;\n\t\t\t}\n\n\t\t\tif ($this->encrypt === TRUE)\n\t\t\t{\n\t\t\t\t$this->dsn .= ';Encrypt=1';\n\t\t\t}\n\n\t\t\tif (isset($this->TraceOn))\n\t\t\t{\n\t\t\t\t$this->dsn .= ';TraceOn='.$this->TraceOn;\n\t\t\t}\n\n\t\t\tif (isset($this->TrustServerCertificate))\n\t\t\t{\n\t\t\t\t$this->dsn .= ';TrustServerCertificate='.$this->TrustServerCertificate;\n\t\t\t}\n\n\t\t\tempty($this->APP) OR $this->dsn .= ';APP='.$this->APP;\n\t\t\tempty($this->Failover_Partner) OR $this->dsn .= ';Failover_Partner='.$this->Failover_Partner;\n\t\t\tempty($this->LoginTimeout) OR $this->dsn .= ';LoginTimeout='.$this->LoginTimeout;\n\t\t\tempty($this->MultipleActiveResultSets) OR $this->dsn .= ';MultipleActiveResultSets='.$this->MultipleActiveResultSets;\n\t\t\tempty($this->TraceFile) OR $this->dsn .= ';TraceFile='.$this->TraceFile;\n\t\t\tempty($this->WSID) OR $this->dsn .= ';WSID='.$this->WSID;\n\t\t}\n\t\telseif (preg_match('/QuotedId=(0|1)/', $this->dsn, $match))\n\t\t{\n\t\t\t$this->_quoted_identifier = (bool) $match[1];\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Database connection\n\t *\n\t * @param\tbool\t$persistent\n\t * @return\tobject\n\t */\n\tpublic function db_connect($persistent = FALSE)\n\t{\n\t\tif ( ! empty($this->char_set) && preg_match('/utf[^8]*8/i', $this->char_set))\n\t\t{\n\t\t\t$this->options[PDO::SQLSRV_ENCODING_UTF8] = 1;\n\t\t}\n\n\t\t$this->conn_id = parent::db_connect($persistent);\n\n\t\tif ( ! is_object($this->conn_id) OR is_bool($this->_quoted_identifier))\n\t\t{\n\t\t\treturn $this->conn_id;\n\t\t}\n\n\t\t// Determine how identifiers are escaped\n\t\t$query = $this->query('SELECT CASE WHEN (@@OPTIONS | 256) = @@OPTIONS THEN 1 ELSE 0 END AS qi');\n\t\t$query = $query->row_array();\n\t\t$this->_quoted_identifier = empty($query) ? FALSE : (bool) $query['qi'];\n\t\t$this->_escape_char = ($this->_quoted_identifier) ? '\"' : array('[', ']');\n\n\t\treturn $this->conn_id;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show table query\n\t *\n\t * Generates a platform-specific query string so that the table names can be fetched\n\t *\n\t * @param\tbool\t$prefix_limit\n\t * @return\tstring\n\t */\n\tprotected function _list_tables($prefix_limit = FALSE)\n\t{\n\t\t$sql = 'SELECT '.$this->escape_identifiers('name')\n\t\t\t.' FROM '.$this->escape_identifiers('sysobjects')\n\t\t\t.' WHERE '.$this->escape_identifiers('type').\" = 'U'\";\n\n\t\tif ($prefix_limit === TRUE && $this->dbprefix !== '')\n\t\t{\n\t\t\t$sql .= ' AND '.$this->escape_identifiers('name').\" LIKE '\".$this->escape_like_str($this->dbprefix).\"%' \"\n\t\t\t\t.sprintf($this->_like_escape_str, $this->_like_escape_chr);\n\t\t}\n\n\t\treturn $sql.' ORDER BY '.$this->escape_identifiers('name');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show column query\n\t *\n\t * Generates a platform-specific query string so that the column names can be fetched\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _list_columns($table = '')\n\t{\n\t\treturn 'SELECT COLUMN_NAME\n\t\t\tFROM INFORMATION_SCHEMA.Columns\n\t\t\tWHERE UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns an object with field data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tarray\n\t */\n\tpublic function field_data($table)\n\t{\n\t\t$sql = 'SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, COLUMN_DEFAULT\n\t\t\tFROM INFORMATION_SCHEMA.Columns\n\t\t\tWHERE UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));\n\n\t\tif (($query = $this->query($sql)) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\t\t$query = $query->result_object();\n\n\t\t$retval = array();\n\t\tfor ($i = 0, $c = count($query); $i < $c; $i++)\n\t\t{\n\t\t\t$retval[$i]\t\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t\t= $query[$i]->COLUMN_NAME;\n\t\t\t$retval[$i]->type\t\t= $query[$i]->DATA_TYPE;\n\t\t\t$retval[$i]->max_length\t\t= ($query[$i]->CHARACTER_MAXIMUM_LENGTH > 0) ? $query[$i]->CHARACTER_MAXIMUM_LENGTH : $query[$i]->NUMERIC_PRECISION;\n\t\t\t$retval[$i]->default\t\t= $query[$i]->COLUMN_DEFAULT;\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Update statement\n\t *\n\t * Generates a platform-specific update string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @param\tarray\t$values\n\t * @return\tstring\n\t */\n\tprotected function _update($table, $values)\n\t{\n\t\t$this->qb_limit = FALSE;\n\t\t$this->qb_orderby = array();\n\t\treturn parent::_update($table, $values);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Delete statement\n\t *\n\t * Generates a platform-specific delete string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _delete($table)\n\t{\n\t\tif ($this->qb_limit)\n\t\t{\n\t\t\treturn 'WITH ci_delete AS (SELECT TOP '.$this->qb_limit.' * FROM '.$table.$this->_compile_wh('qb_where').') DELETE FROM ci_delete';\n\t\t}\n\n\t\treturn parent::_delete($table);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * LIMIT\n\t *\n\t * Generates a platform-specific LIMIT clause\n\t *\n\t * @param\tstring\t$sql\tSQL Query\n\t * @return\tstring\n\t */\n\tprotected function _limit($sql)\n\t{\n\t\t// As of SQL Server 2012 (11.0.*) OFFSET is supported\n\t\tif (version_compare($this->version(), '11', '>='))\n\t\t{\n\t\t\t// SQL Server OFFSET-FETCH can be used only with the ORDER BY clause\n\t\t\tempty($this->qb_orderby) && $sql .= ' ORDER BY 1';\n\n\t\t\treturn $sql.' OFFSET '.(int) $this->qb_offset.' ROWS FETCH NEXT '.$this->qb_limit.' ROWS ONLY';\n\t\t}\n\n\t\t$limit = $this->qb_offset + $this->qb_limit;\n\n\t\t// An ORDER BY clause is required for ROW_NUMBER() to work\n\t\tif ($this->qb_offset && ! empty($this->qb_orderby))\n\t\t{\n\t\t\t$orderby = $this->_compile_order_by();\n\n\t\t\t// We have to strip the ORDER BY clause\n\t\t\t$sql = trim(substr($sql, 0, strrpos($sql, $orderby)));\n\n\t\t\t// Get the fields to select from our subquery, so that we can avoid CI_rownum appearing in the actual results\n\t\t\tif (count($this->qb_select) === 0 OR strpos(implode(',', $this->qb_select), '*') !== FALSE)\n\t\t\t{\n\t\t\t\t$select = '*'; // Inevitable\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Use only field names and their aliases, everything else is out of our scope.\n\t\t\t\t$select = array();\n\t\t\t\t$field_regexp = ($this->_quoted_identifier)\n\t\t\t\t\t? '(\"[^\\\"]+\")' : '(\\[[^\\]]+\\])';\n\t\t\t\tfor ($i = 0, $c = count($this->qb_select); $i < $c; $i++)\n\t\t\t\t{\n\t\t\t\t\t$select[] = preg_match('/(?:\\s|\\.)'.$field_regexp.'$/i', $this->qb_select[$i], $m)\n\t\t\t\t\t\t? $m[1] : $this->qb_select[$i];\n\t\t\t\t}\n\t\t\t\t$select = implode(', ', $select);\n\t\t\t}\n\n\t\t\treturn 'SELECT '.$select.\" FROM (\\n\\n\"\n\t\t\t\t.preg_replace('/^(SELECT( DISTINCT)?)/i', '\\\\1 ROW_NUMBER() OVER('.trim($orderby).') AS '.$this->escape_identifiers('CI_rownum').', ', $sql)\n\t\t\t\t.\"\\n\\n) \".$this->escape_identifiers('CI_subquery')\n\t\t\t\t.\"\\nWHERE \".$this->escape_identifiers('CI_rownum').' BETWEEN '.($this->qb_offset + 1).' AND '.$limit;\n\t\t}\n\n\t\treturn preg_replace('/(^\\SELECT (DISTINCT)?)/i','\\\\1 TOP '.$limit.' ', $sql);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert batch statement\n\t *\n\t * Generates a platform-specific insert string from the supplied data.\n\t *\n\t * @param\tstring\t$table\tTable name\n\t * @param\tarray\t$keys\tINSERT keys\n\t * @param\tarray\t$values\tINSERT values\n\t * @return\tstring|bool\n\t */\n\tprotected function _insert_batch($table, $keys, $values)\n\t{\n\t\t// Multiple-value inserts are only supported as of SQL Server 2008\n\t\tif (version_compare($this->version(), '10', '>='))\n\t\t{\n\t\t\treturn parent::_insert_batch($table, $keys, $values);\n\t\t}\n\n\t\treturn ($this->db_debug) ? $this->display_error('db_unsupported_feature') : FALSE;\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/pdo/subdrivers/pdo_sqlsrv_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PDO SQLSRV Forge Class\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_pdo_sqlsrv_forge extends CI_DB_pdo_forge {\n\n\t/**\n\t * CREATE TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_table_if\t= \"IF NOT EXISTS (SELECT * FROM sysobjects WHERE ID = object_id(N'%s') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)\\nCREATE TABLE\";\n\n\t/**\n\t * DROP TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_drop_table_if\t= \"IF EXISTS (SELECT * FROM sysobjects WHERE ID = object_id(N'%s') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)\\nDROP TABLE\";\n\n\t/**\n\t * UNSIGNED support\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_unsigned\t\t= array(\n\t\t'TINYINT'\t=> 'SMALLINT',\n\t\t'SMALLINT'\t=> 'INT',\n\t\t'INT'\t\t=> 'BIGINT',\n\t\t'REAL'\t\t=> 'FLOAT'\n\t);\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ALTER TABLE\n\t *\n\t * @param\tstring\t$alter_type\tALTER type\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tmixed\t$field\t\tColumn definition\n\t * @return\tstring|string[]\n\t */\n\tprotected function _alter_table($alter_type, $table, $field)\n\t{\n\t\tif (in_array($alter_type, array('ADD', 'DROP'), TRUE))\n\t\t{\n\t\t\treturn parent::_alter_table($alter_type, $table, $field);\n\t\t}\n\n\t\t$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' ALTER COLUMN ';\n\t\t$sqls = array();\n\t\tfor ($i = 0, $c = count($field); $i < $c; $i++)\n\t\t{\n\t\t\t$sqls[] = $sql.$this->_process_column($field[$i]);\n\t\t}\n\n\t\treturn $sqls;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute TYPE\n\t *\n\t * Performs a data type mapping between different databases.\n\t *\n\t * @param\tarray\t&$attributes\n\t * @return\tvoid\n\t */\n\tprotected function _attr_type(&$attributes)\n\t{\n\t\tif (isset($attributes['CONSTRAINT']) && strpos($attributes['TYPE'], 'INT') !== FALSE)\n\t\t{\n\t\t\tunset($attributes['CONSTRAINT']);\n\t\t}\n\n\t\tswitch (strtoupper($attributes['TYPE']))\n\t\t{\n\t\t\tcase 'MEDIUMINT':\n\t\t\t\t$attributes['TYPE'] = 'INTEGER';\n\t\t\t\t$attributes['UNSIGNED'] = FALSE;\n\t\t\t\treturn;\n\t\t\tcase 'INTEGER':\n\t\t\t\t$attributes['TYPE'] = 'INT';\n\t\t\t\treturn;\n\t\t\tdefault: return;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute AUTO_INCREMENT\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_auto_increment(&$attributes, &$field)\n\t{\n\t\tif ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE)\n\t\t{\n\t\t\t$field['auto_increment'] = ' IDENTITY(1,1)';\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/postgre/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/database/drivers/postgre/postgre_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.3.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Postgre Database Adapter Class\n *\n * Note: _DB is an extender class that the app controller\n * creates dynamically based on whether the query builder\n * class is being used or not.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_postgre_driver extends CI_DB {\n\n\t/**\n\t * Database driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $dbdriver = 'postgre';\n\n\t/**\n\t * Database schema\n\t *\n\t * @var\tstring\n\t */\n\tpublic $schema = 'public';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ORDER BY random keyword\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_random_keyword = array('RANDOM()', 'RANDOM()');\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Build DSN\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _build_dsn()\n\t{\n\t\t$this->dsn === '' OR $this->dsn = '';\n\n\t\tif (strpos($this->hostname, '/') !== FALSE)\n\t\t{\n\t\t\t// If UNIX sockets are used, we shouldn't set a port\n\t\t\t$this->port = '';\n\t\t}\n\n\t\t$this->hostname === '' OR $this->dsn = 'host='.$this->hostname.' ';\n\n\t\tif ( ! empty($this->port) && ctype_digit($this->port))\n\t\t{\n\t\t\t$this->dsn .= 'port='.$this->port.' ';\n\t\t}\n\n\t\tif ($this->username !== '')\n\t\t{\n\t\t\t$this->dsn .= 'user='.$this->username.' ';\n\n\t\t\t/* An empty password is valid!\n\t\t\t *\n\t\t\t * $db['password'] = NULL must be done in order to ignore it.\n\t\t\t */\n\t\t\t$this->password === NULL OR $this->dsn .= \"password='\".$this->password.\"' \";\n\t\t}\n\n\t\t$this->database === '' OR $this->dsn .= 'dbname='.$this->database.' ';\n\n\t\t/* We don't have these options as elements in our standard configuration\n\t\t * array, but they might be set by parse_url() if the configuration was\n\t\t * provided via string. Example:\n\t\t *\n\t\t * postgre://username:password@localhost:5432/database?connect_timeout=5&sslmode=1\n\t\t */\n\t\tforeach (array('connect_timeout', 'options', 'sslmode', 'service') as $key)\n\t\t{\n\t\t\tif (isset($this->$key) && is_string($this->$key) && $this->$key !== '')\n\t\t\t{\n\t\t\t\t$this->dsn .= $key.\"='\".$this->$key.\"' \";\n\t\t\t}\n\t\t}\n\n\t\t$this->dsn = rtrim($this->dsn);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Database connection\n\t *\n\t * @param\tbool\t$persistent\n\t * @return\tresource|object\n\t */\n\tpublic function db_connect($persistent = FALSE)\n\t{\n\t\tempty($this->dsn) && $this->_build_dsn();\n\t\t$this->conn_id = ($persistent === TRUE)\n\t\t\t? pg_pconnect($this->dsn)\n\t\t\t: pg_connect($this->dsn);\n\n\t\tif ($this->conn_id !== FALSE)\n\t\t{\n\t\t\tif ($persistent === TRUE\n\t\t\t\t&& pg_connection_status($this->conn_id) === PGSQL_CONNECTION_BAD\n\t\t\t\t&& pg_ping($this->conn_id) === FALSE\n\t\t\t)\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\tif (pg_set_client_encoding($this->conn_id, $this->char_set) !== 0)\n\t\t\t{\n\t\t\t\tlog_message('error', \"Database: Unable to set the configured connection charset ('{$this->char_set}').\");\n\t\t\t\tpg_close($this->conn_id);\n\t\t\t\treturn ($this->db->db_debug) ? $this->display_error('db_unable_to_set_charset', $this->char_set) : FALSE;\n\t\t\t}\n\n\t\t\tempty($this->schema) OR $this->simple_query('SET search_path TO '.$this->schema.',public');\n\t\t}\n\n\t\treturn $this->conn_id;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Reconnect\n\t *\n\t * Keep / reestablish the db connection if no queries have been\n\t * sent for a length of time exceeding the server's idle timeout\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function reconnect()\n\t{\n\t\tif (pg_ping($this->conn_id) === FALSE)\n\t\t{\n\t\t\t$this->conn_id = FALSE;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Database version number\n\t *\n\t * @return\tstring\n\t */\n\tpublic function version()\n\t{\n\t\tif (isset($this->data_cache['version']))\n\t\t{\n\t\t\treturn $this->data_cache['version'];\n\t\t}\n\n\t\tif ( ! $this->conn_id OR ($pg_version = pg_version($this->conn_id)) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t/* If PHP was compiled with PostgreSQL lib versions earlier\n\t\t * than 7.4, pg_version() won't return the server version\n\t\t * and so we'll have to fall back to running a query in\n\t\t * order to get it.\n\t\t */\n\t\treturn (isset($pg_version['server']) && preg_match('#^(\\d+\\.\\d+)#', $pg_version['server'], $match))\n\t\t\t? $this->data_cache['version'] = $match[1]\n\t\t\t: parent::version();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Execute the query\n\t *\n\t * @param\tstring\t$sql\tan SQL query\n\t * @return\tresource|object\n\t */\n\tprotected function _execute($sql)\n\t{\n\t\treturn pg_query($this->conn_id, $sql);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Begin Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_begin()\n\t{\n\t\treturn (bool) pg_query($this->conn_id, 'BEGIN');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Commit Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_commit()\n\t{\n\t\treturn (bool) pg_query($this->conn_id, 'COMMIT');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Rollback Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_rollback()\n\t{\n\t\treturn (bool) pg_query($this->conn_id, 'ROLLBACK');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Determines if a query is a \"write\" type.\n\t *\n\t * @param\tstring\tAn SQL query string\n\t * @return\tbool\n\t */\n\tpublic function is_write_type($sql)\n\t{\n\t\tif (preg_match('#^(INSERT|UPDATE).*RETURNING\\s.+(\\,\\s?.+)*$#is', $sql))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn parent::is_write_type($sql);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Platform-dependent string escape\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tprotected function _escape_str($str)\n\t{\n\t\treturn pg_escape_string($this->conn_id, $str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * \"Smart\" Escape String\n\t *\n\t * Escapes data based on type\n\t *\n\t * @param\tstring\t$str\n\t * @return\tmixed\n\t */\n\tpublic function escape($str)\n\t{\n\t\tif (is_string($str) OR (is_object($str) && method_exists($str, '__toString')))\n\t\t{\n\t\t\treturn pg_escape_literal($this->conn_id, $str);\n\t\t}\n\t\telseif (is_bool($str))\n\t\t{\n\t\t\treturn ($str) ? 'TRUE' : 'FALSE';\n\t\t}\n\n\t\treturn parent::escape($str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Affected Rows\n\t *\n\t * @return\tint\n\t */\n\tpublic function affected_rows()\n\t{\n\t\treturn pg_affected_rows($this->result_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert ID\n\t *\n\t * @return\tstring\n\t */\n\tpublic function insert_id()\n\t{\n\t\t$v = $this->version();\n\n\t\t$table\t= (func_num_args() > 0) ? func_get_arg(0) : NULL;\n\t\t$column\t= (func_num_args() > 1) ? func_get_arg(1) : NULL;\n\n\t\tif ($table === NULL && $v >= '8.1')\n\t\t{\n\t\t\t$sql = 'SELECT LASTVAL() AS ins_id';\n\t\t}\n\t\telseif ($table !== NULL)\n\t\t{\n\t\t\tif ($column !== NULL && $v >= '8.0')\n\t\t\t{\n\t\t\t\t$sql = 'SELECT pg_get_serial_sequence(\\''.$table.\"', '\".$column.\"') AS seq\";\n\t\t\t\t$query = $this->query($sql);\n\t\t\t\t$query = $query->row();\n\t\t\t\t$seq = $query->seq;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// seq_name passed in table parameter\n\t\t\t\t$seq = $table;\n\t\t\t}\n\n\t\t\t$sql = 'SELECT CURRVAL(\\''.$seq.\"') AS ins_id\";\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn pg_last_oid($this->result_id);\n\t\t}\n\n\t\t$query = $this->query($sql);\n\t\t$query = $query->row();\n\t\treturn (int) $query->ins_id;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show table query\n\t *\n\t * Generates a platform-specific query string so that the table names can be fetched\n\t *\n\t * @param\tbool\t$prefix_limit\n\t * @return\tstring\n\t */\n\tprotected function _list_tables($prefix_limit = FALSE)\n\t{\n\t\t$sql = 'SELECT \"table_name\" FROM \"information_schema\".\"tables\" WHERE \"table_schema\" = \\''.$this->schema.\"'\";\n\n\t\tif ($prefix_limit !== FALSE && $this->dbprefix !== '')\n\t\t{\n\t\t\treturn $sql.' AND \"table_name\" LIKE \\''\n\t\t\t\t.$this->escape_like_str($this->dbprefix).\"%' \"\n\t\t\t\t.sprintf($this->_like_escape_str, $this->_like_escape_chr);\n\t\t}\n\n\t\treturn $sql;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * List column query\n\t *\n\t * Generates a platform-specific query string so that the column names can be fetched\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _list_columns($table = '')\n\t{\n\t\treturn 'SELECT \"column_name\"\n\t\t\tFROM \"information_schema\".\"columns\"\n\t\t\tWHERE \"table_schema\" = \\''.$this->schema.'\\' AND LOWER(\"table_name\") = '.$this->escape(strtolower($table));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns an object with field data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tarray\n\t */\n\tpublic function field_data($table)\n\t{\n\t\t$sql = 'SELECT \"column_name\", \"data_type\", \"character_maximum_length\", \"numeric_precision\", \"column_default\"\n\t\t\tFROM \"information_schema\".\"columns\"\n\t\t\tWHERE \"table_schema\" = \\''.$this->schema.'\\' AND LOWER(\"table_name\") = '.$this->escape(strtolower($table));\n\n\t\tif (($query = $this->query($sql)) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\t\t$query = $query->result_object();\n\n\t\t$retval = array();\n\t\tfor ($i = 0, $c = count($query); $i < $c; $i++)\n\t\t{\n\t\t\t$retval[$i]\t\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t\t= $query[$i]->column_name;\n\t\t\t$retval[$i]->type\t\t= $query[$i]->data_type;\n\t\t\t$retval[$i]->max_length\t\t= ($query[$i]->character_maximum_length > 0) ? $query[$i]->character_maximum_length : $query[$i]->numeric_precision;\n\t\t\t$retval[$i]->default\t\t= $query[$i]->column_default;\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Error\n\t *\n\t * Returns an array containing code and message of the last\n\t * database error that has occurred.\n\t *\n\t * @return\tarray\n\t */\n\tpublic function error()\n\t{\n\t\treturn array('code' => '', 'message' => pg_last_error($this->conn_id));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ORDER BY\n\t *\n\t * @param\tstring\t$orderby\n\t * @param\tstring\t$direction\tASC, DESC or RANDOM\n\t * @param\tbool\t$escape\n\t * @return\tobject\n\t */\n\tpublic function order_by($orderby, $direction = '', $escape = NULL)\n\t{\n\t\t$direction = strtoupper(trim($direction));\n\t\tif ($direction === 'RANDOM')\n\t\t{\n\t\t\tif ( ! is_float($orderby) && ctype_digit((string) $orderby))\n\t\t\t{\n\t\t\t\t$orderby = ($orderby > 1)\n\t\t\t\t\t? (float) '0.'.$orderby\n\t\t\t\t\t: (float) $orderby;\n\t\t\t}\n\n\t\t\tif (is_float($orderby))\n\t\t\t{\n\t\t\t\t$this->simple_query('SET SEED '.$orderby);\n\t\t\t}\n\n\t\t\t$orderby = $this->_random_keyword[0];\n\t\t\t$direction = '';\n\t\t\t$escape = FALSE;\n\t\t}\n\n\t\treturn parent::order_by($orderby, $direction, $escape);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Update statement\n\t *\n\t * Generates a platform-specific update string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @param\tarray\t$values\n\t * @return\tstring\n\t */\n\tprotected function _update($table, $values)\n\t{\n\t\t$this->qb_limit = FALSE;\n\t\t$this->qb_orderby = array();\n\t\treturn parent::_update($table, $values);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Update_Batch statement\n\t *\n\t * Generates a platform-specific batch update string from the supplied data\n\t *\n\t * @param\tstring\t$table\tTable name\n\t * @param\tarray\t$values\tUpdate data\n\t * @param\tstring\t$index\tWHERE key\n\t * @return\tstring\n\t */\n\tprotected function _update_batch($table, $values, $index)\n\t{\n\t\t$ids = array();\n\t\tforeach ($values as $key => $val)\n\t\t{\n\t\t\t$ids[] = $val[$index]['value'];\n\n\t\t\tforeach (array_keys($val) as $field)\n\t\t\t{\n\t\t\t\tif ($field !== $index)\n\t\t\t\t{\n\t\t\t\t\t$final[$val[$field]['field']][] = 'WHEN '.$val[$index]['value'].' THEN '.$val[$field]['value'];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$cases = '';\n\t\tforeach ($final as $k => $v)\n\t\t{\n\t\t\t$cases .= $k.' = (CASE '.$val[$index]['field'].\"\\n\"\n\t\t\t\t.implode(\"\\n\", $v).\"\\n\"\n\t\t\t\t.'ELSE '.$k.' END), ';\n\t\t}\n\n\t\t$this->where($val[$index]['field'].' IN('.implode(',', $ids).')', NULL, FALSE);\n\n\t\treturn 'UPDATE '.$table.' SET '.substr($cases, 0, -2).$this->_compile_wh('qb_where');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Delete statement\n\t *\n\t * Generates a platform-specific delete string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _delete($table)\n\t{\n\t\t$this->qb_limit = FALSE;\n\t\treturn parent::_delete($table);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * LIMIT\n\t *\n\t * Generates a platform-specific LIMIT clause\n\t *\n\t * @param\tstring\t$sql\tSQL Query\n\t * @return\tstring\n\t */\n\tprotected function _limit($sql)\n\t{\n\t\treturn $sql.' LIMIT '.$this->qb_limit.($this->qb_offset ? ' OFFSET '.$this->qb_offset : '');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Close DB Connection\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _close()\n\t{\n\t\tpg_close($this->conn_id);\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/postgre/postgre_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.3.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Postgre Forge Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_postgre_forge extends CI_DB_forge {\n\n\t/**\n\t * UNSIGNED support\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_unsigned\t\t= array(\n\t\t'INT2'\t\t=> 'INTEGER',\n\t\t'SMALLINT'\t=> 'INTEGER',\n\t\t'INT'\t\t=> 'BIGINT',\n\t\t'INT4'\t\t=> 'BIGINT',\n\t\t'INTEGER'\t=> 'BIGINT',\n\t\t'INT8'\t\t=> 'NUMERIC',\n\t\t'BIGINT'\t=> 'NUMERIC',\n\t\t'REAL'\t\t=> 'DOUBLE PRECISION',\n\t\t'FLOAT'\t\t=> 'DOUBLE PRECISION'\n\t);\n\n\t/**\n\t * NULL value representation in CREATE/ALTER TABLE statements\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_null = 'NULL';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * @param\tobject\t&$db\tDatabase object\n\t * @return\tvoid\n\t */\n\tpublic function __construct(&$db)\n\t{\n\t\tparent::__construct($db);\n\n\t\tif (version_compare($this->db->version(), '9.0', '>'))\n\t\t{\n\t\t\t$this->create_table_if = 'CREATE TABLE IF NOT EXISTS';\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ALTER TABLE\n\t *\n\t * @param\tstring\t$alter_type\tALTER type\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tmixed\t$field\t\tColumn definition\n\t * @return\tstring|string[]\n\t */\n\tprotected function _alter_table($alter_type, $table, $field)\n\t{\n\t\tif (in_array($alter_type, array('DROP', 'ADD'), TRUE))\n\t\t{\n\t\t\treturn parent::_alter_table($alter_type, $table, $field);\n\t\t}\n\n\t\t$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table);\n\t\t$sqls = array();\n\t\tfor ($i = 0, $c = count($field); $i < $c; $i++)\n\t\t{\n\t\t\tif ($field[$i]['_literal'] !== FALSE)\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\tif (version_compare($this->db->version(), '8', '>=') && isset($field[$i]['type']))\n\t\t\t{\n\t\t\t\t$sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identifiers($field[$i]['name'])\n\t\t\t\t\t.' TYPE '.$field[$i]['type'].$field[$i]['length'];\n\t\t\t}\n\n\t\t\tif ( ! empty($field[$i]['default']))\n\t\t\t{\n\t\t\t\t$sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identifiers($field[$i]['name'])\n\t\t\t\t\t.' SET '.$field[$i]['default'];\n\t\t\t}\n\n\t\t\tif (isset($field[$i]['null']))\n\t\t\t{\n\t\t\t\t$sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identifiers($field[$i]['name'])\n\t\t\t\t\t.(trim($field[$i]['null']) === $this->_null ? ' DROP NOT NULL' : ' SET NOT NULL');\n\t\t\t}\n\n\t\t\tif ( ! empty($field[$i]['new_name']))\n\t\t\t{\n\t\t\t\t$sqls[] = $sql.' RENAME COLUMN '.$this->db->escape_identifiers($field[$i]['name'])\n\t\t\t\t\t.' TO '.$this->db->escape_identifiers($field[$i]['new_name']);\n\t\t\t}\n\n\t\t\tif ( ! empty($field[$i]['comment']))\n\t\t\t{\n\t\t\t\t$sqls[] = 'COMMENT ON COLUMN '\n\t\t\t\t\t.$this->db->escape_identifiers($table).'.'.$this->db->escape_identifiers($field[$i]['name'])\n\t\t\t\t\t.' IS '.$field[$i]['comment'];\n\t\t\t}\n\t\t}\n\n\t\treturn $sqls;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute TYPE\n\t *\n\t * Performs a data type mapping between different databases.\n\t *\n\t * @param\tarray\t&$attributes\n\t * @return\tvoid\n\t */\n\tprotected function _attr_type(&$attributes)\n\t{\n\t\t// Reset field lengths for data types that don't support it\n\t\tif (isset($attributes['CONSTRAINT']) && stripos($attributes['TYPE'], 'int') !== FALSE)\n\t\t{\n\t\t\t$attributes['CONSTRAINT'] = NULL;\n\t\t}\n\n\t\tswitch (strtoupper($attributes['TYPE']))\n\t\t{\n\t\t\tcase 'TINYINT':\n\t\t\t\t$attributes['TYPE'] = 'SMALLINT';\n\t\t\t\t$attributes['UNSIGNED'] = FALSE;\n\t\t\t\treturn;\n\t\t\tcase 'MEDIUMINT':\n\t\t\t\t$attributes['TYPE'] = 'INTEGER';\n\t\t\t\t$attributes['UNSIGNED'] = FALSE;\n\t\t\t\treturn;\n\t\t\tdefault: return;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute AUTO_INCREMENT\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_auto_increment(&$attributes, &$field)\n\t{\n\t\tif ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE)\n\t\t{\n\t\t\t$field['type'] = ($field['type'] === 'NUMERIC')\n\t\t\t\t? 'BIGSERIAL'\n\t\t\t\t: 'SERIAL';\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/postgre/postgre_result.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.3.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Postgres Result Class\n *\n * This class extends the parent result class: CI_DB_result\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_postgre_result extends CI_DB_result {\n\n\t/**\n\t * Number of rows in the result set\n\t *\n\t * @return\tint\n\t */\n\tpublic function num_rows()\n\t{\n\t\treturn is_int($this->num_rows)\n\t\t\t? $this->num_rows\n\t\t\t: $this->num_rows = pg_num_rows($this->result_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Number of fields in the result set\n\t *\n\t * @return\tint\n\t */\n\tpublic function num_fields()\n\t{\n\t\treturn pg_num_fields($this->result_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch Field Names\n\t *\n\t * Generates an array of column names\n\t *\n\t * @return\tarray\n\t */\n\tpublic function list_fields()\n\t{\n\t\t$field_names = array();\n\t\tfor ($i = 0, $c = $this->num_fields(); $i < $c; $i++)\n\t\t{\n\t\t\t$field_names[] = pg_field_name($this->result_id, $i);\n\t\t}\n\n\t\treturn $field_names;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field data\n\t *\n\t * Generates an array of objects containing field meta-data\n\t *\n\t * @return\tarray\n\t */\n\tpublic function field_data()\n\t{\n\t\t$retval = array();\n\t\tfor ($i = 0, $c = $this->num_fields(); $i < $c; $i++)\n\t\t{\n\t\t\t$retval[$i]\t\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t\t= pg_field_name($this->result_id, $i);\n\t\t\t$retval[$i]->type\t\t= pg_field_type($this->result_id, $i);\n\t\t\t$retval[$i]->max_length\t\t= pg_field_size($this->result_id, $i);\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Free the result\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function free_result()\n\t{\n\t\tif ($this->result_id !== FALSE)\n\t\t{\n\t\t\tpg_free_result($this->result_id);\n\t\t\t$this->result_id = FALSE;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Data Seek\n\t *\n\t * Moves the internal pointer to the desired offset. We call\n\t * this internally before fetching results to make sure the\n\t * result set starts at zero.\n\t *\n\t * @param\tint\t$n\n\t * @return\tbool\n\t */\n\tpublic function data_seek($n = 0)\n\t{\n\t\treturn pg_result_seek($this->result_id, $n);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - associative array\n\t *\n\t * Returns the result set as an array\n\t *\n\t * @return\tarray\n\t */\n\tprotected function _fetch_assoc()\n\t{\n\t\treturn pg_fetch_assoc($this->result_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - object\n\t *\n\t * Returns the result set as an object\n\t *\n\t * @param\tstring\t$class_name\n\t * @return\tobject\n\t */\n\tprotected function _fetch_object($class_name = 'stdClass')\n\t{\n\t\treturn pg_fetch_object($this->result_id, NULL, $class_name);\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/postgre/postgre_utility.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.3.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Postgre Utility Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_postgre_utility extends CI_DB_utility {\n\n\t/**\n\t * List databases statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_list_databases\t= 'SELECT datname FROM pg_database';\n\n\t/**\n\t * OPTIMIZE TABLE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_optimize_table\t= 'REINDEX TABLE %s';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Export\n\t *\n\t * @param\tarray\t$params\tPreferences\n\t * @return\tmixed\n\t */\n\tprotected function _backup($params = array())\n\t{\n\t\t// Currently unsupported\n\t\treturn $this->db->display_error('db_unsupported_feature');\n\t}\n}\n"
  },
  {
    "path": "system/database/drivers/sqlite3/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/database/drivers/sqlite3/sqlite3_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * SQLite3 Database Adapter Class\n *\n * Note: _DB is an extender class that the app controller\n * creates dynamically based on whether the query builder\n * class is being used or not.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tAndrey Andreev\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_sqlite3_driver extends CI_DB {\n\n\t/**\n\t * Database driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $dbdriver = 'sqlite3';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ORDER BY random keyword\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_random_keyword = array('RANDOM()', 'RANDOM()');\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Non-persistent database connection\n\t *\n\t * @param\tbool\t$persistent\n\t * @return\tSQLite3\n\t */\n\tpublic function db_connect($persistent = FALSE)\n\t{\n\t\tif ($persistent)\n\t\t{\n\t\t\tlog_message('debug', 'SQLite3 doesn\\'t support persistent connections');\n\t\t}\n\n\t\ttry\n\t\t{\n\t\t\treturn ( ! $this->password)\n\t\t\t\t? new SQLite3($this->database)\n\t\t\t\t: new SQLite3($this->database, SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE, $this->password);\n\t\t}\n\t\tcatch (Exception $e)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Database version number\n\t *\n\t * @return\tstring\n\t */\n\tpublic function version()\n\t{\n\t\tif (isset($this->data_cache['version']))\n\t\t{\n\t\t\treturn $this->data_cache['version'];\n\t\t}\n\n\t\t$version = SQLite3::version();\n\t\treturn $this->data_cache['version'] = $version['versionString'];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Execute the query\n\t *\n\t * @todo\tImplement use of SQLite3::querySingle(), if needed\n\t * @param\tstring\t$sql\n\t * @return\tmixed\tSQLite3Result object or bool\n\t */\n\tprotected function _execute($sql)\n\t{\n\t\treturn $this->is_write_type($sql)\n\t\t\t? $this->conn_id->exec($sql)\n\t\t\t: $this->conn_id->query($sql);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Begin Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_begin()\n\t{\n\t\treturn $this->conn_id->exec('BEGIN TRANSACTION');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Commit Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_commit()\n\t{\n\t\treturn $this->conn_id->exec('END TRANSACTION');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Rollback Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_rollback()\n\t{\n\t\treturn $this->conn_id->exec('ROLLBACK');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Platform-dependent string escape\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tprotected function _escape_str($str)\n\t{\n\t\treturn $this->conn_id->escapeString($str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Affected Rows\n\t *\n\t * @return\tint\n\t */\n\tpublic function affected_rows()\n\t{\n\t\treturn $this->conn_id->changes();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert ID\n\t *\n\t * @return\tint\n\t */\n\tpublic function insert_id()\n\t{\n\t\treturn $this->conn_id->lastInsertRowID();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show table query\n\t *\n\t * Generates a platform-specific query string so that the table names can be fetched\n\t *\n\t * @param\tbool\t$prefix_limit\n\t * @return\tstring\n\t */\n\tprotected function _list_tables($prefix_limit = FALSE)\n\t{\n\t\treturn 'SELECT \"NAME\" FROM \"SQLITE_MASTER\" WHERE \"TYPE\" = \\'table\\''\n\t\t\t.(($prefix_limit !== FALSE && $this->dbprefix != '')\n\t\t\t\t? ' AND \"NAME\" LIKE \\''.$this->escape_like_str($this->dbprefix).'%\\' '.sprintf($this->_like_escape_str, $this->_like_escape_chr)\n\t\t\t\t: '');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch Field Names\n\t *\n\t * @param\tstring\t$table\tTable name\n\t * @return\tarray\n\t */\n\tpublic function list_fields($table)\n\t{\n\t\tif (($result = $this->query('PRAGMA TABLE_INFO('.$this->protect_identifiers($table, TRUE, NULL, FALSE).')')) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$fields = array();\n\t\tforeach ($result->result_array() as $row)\n\t\t{\n\t\t\t$fields[] = $row['name'];\n\t\t}\n\n\t\treturn $fields;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns an object with field data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tarray\n\t */\n\tpublic function field_data($table)\n\t{\n\t\tif (($query = $this->query('PRAGMA TABLE_INFO('.$this->protect_identifiers($table, TRUE, NULL, FALSE).')')) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$query = $query->result_array();\n\t\tif (empty($query))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$retval = array();\n\t\tfor ($i = 0, $c = count($query); $i < $c; $i++)\n\t\t{\n\t\t\t$retval[$i]\t\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t\t= $query[$i]['name'];\n\t\t\t$retval[$i]->type\t\t= $query[$i]['type'];\n\t\t\t$retval[$i]->max_length\t\t= NULL;\n\t\t\t$retval[$i]->default\t\t= $query[$i]['dflt_value'];\n\t\t\t$retval[$i]->primary_key\t= isset($query[$i]['pk']) ? (int) $query[$i]['pk'] : 0;\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Error\n\t *\n\t * Returns an array containing code and message of the last\n\t * database error that has occurred.\n\t *\n\t * @return\tarray\n\t */\n\tpublic function error()\n\t{\n\t\treturn array('code' => $this->conn_id->lastErrorCode(), 'message' => $this->conn_id->lastErrorMsg());\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Replace statement\n\t *\n\t * Generates a platform-specific replace string from the supplied data\n\t *\n\t * @param\tstring\t$table\tTable name\n\t * @param\tarray\t$keys\tINSERT keys\n\t * @param\tarray\t$values\tINSERT values\n\t * @return\tstring\n\t */\n\tprotected function _replace($table, $keys, $values)\n\t{\n\t\treturn 'INSERT OR '.parent::_replace($table, $keys, $values);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Truncate statement\n\t *\n\t * Generates a platform-specific truncate string from the supplied data\n\t *\n\t * If the database does not support the TRUNCATE statement,\n\t * then this method maps to 'DELETE FROM table'\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _truncate($table)\n\t{\n\t\treturn 'DELETE FROM '.$table;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Close DB Connection\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _close()\n\t{\n\t\t$this->conn_id->close();\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/sqlite3/sqlite3_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * SQLite3 Forge Class\n *\n * @category\tDatabase\n * @author\tAndrey Andreev\n * @link\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_sqlite3_forge extends CI_DB_forge {\n\n\t/**\n\t * UNSIGNED support\n\t *\n\t * @var\tbool|array\n\t */\n\tprotected $_unsigned\t\t= FALSE;\n\n\t/**\n\t * NULL value representation in CREATE/ALTER TABLE statements\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_null\t\t= 'NULL';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * @param\tobject\t&$db\tDatabase object\n\t * @return\tvoid\n\t */\n\tpublic function __construct(&$db)\n\t{\n\t\tparent::__construct($db);\n\n\t\tif (version_compare($this->db->version(), '3.3', '<'))\n\t\t{\n\t\t\t$this->_create_table_if = FALSE;\n\t\t\t$this->_drop_table_if   = FALSE;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Create database\n\t *\n\t * @param\tstring\t$db_name\n\t * @return\tbool\n\t */\n\tpublic function create_database($db_name)\n\t{\n\t\t// In SQLite, a database is created when you connect to the database.\n\t\t// We'll return TRUE so that an error isn't generated\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Drop database\n\t *\n\t * @param\tstring\t$db_name\t(ignored)\n\t * @return\tbool\n\t */\n\tpublic function drop_database($db_name)\n\t{\n\t\t// In SQLite, a database is dropped when we delete a file\n\t\tif (file_exists($this->db->database))\n\t\t{\n\t\t\t// We need to close the pseudo-connection first\n\t\t\t$this->db->close();\n\t\t\tif ( ! @unlink($this->db->database))\n\t\t\t{\n\t\t\t\treturn $this->db->db_debug ? $this->db->display_error('db_unable_to_drop') : FALSE;\n\t\t\t}\n\t\t\telseif ( ! empty($this->db->data_cache['db_names']))\n\t\t\t{\n\t\t\t\t$key = array_search(strtolower($this->db->database), array_map('strtolower', $this->db->data_cache['db_names']), TRUE);\n\t\t\t\tif ($key !== FALSE)\n\t\t\t\t{\n\t\t\t\t\tunset($this->db->data_cache['db_names'][$key]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn $this->db->db_debug ? $this->db->display_error('db_unable_to_drop') : FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ALTER TABLE\n\t *\n\t * @todo\timplement drop_column(), modify_column()\n\t * @param\tstring\t$alter_type\tALTER type\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tmixed\t$field\t\tColumn definition\n\t * @return\tstring|string[]\n\t */\n\tprotected function _alter_table($alter_type, $table, $field)\n\t{\n\t\tif ($alter_type === 'DROP' OR $alter_type === 'CHANGE')\n\t\t{\n\t\t\t// drop_column():\n\t\t\t//\tBEGIN TRANSACTION;\n\t\t\t//\tCREATE TEMPORARY TABLE t1_backup(a,b);\n\t\t\t//\tINSERT INTO t1_backup SELECT a,b FROM t1;\n\t\t\t//\tDROP TABLE t1;\n\t\t\t//\tCREATE TABLE t1(a,b);\n\t\t\t//\tINSERT INTO t1 SELECT a,b FROM t1_backup;\n\t\t\t//\tDROP TABLE t1_backup;\n\t\t\t//\tCOMMIT;\n\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn parent::_alter_table($alter_type, $table, $field);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Process column\n\t *\n\t * @param\tarray\t$field\n\t * @return\tstring\n\t */\n\tprotected function _process_column($field)\n\t{\n\t\treturn $this->db->escape_identifiers($field['name'])\n\t\t\t.' '.$field['type']\n\t\t\t.$field['auto_increment']\n\t\t\t.$field['null']\n\t\t\t.$field['unique']\n\t\t\t.$field['default'];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute TYPE\n\t *\n\t * Performs a data type mapping between different databases.\n\t *\n\t * @param\tarray\t&$attributes\n\t * @return\tvoid\n\t */\n\tprotected function _attr_type(&$attributes)\n\t{\n\t\tswitch (strtoupper($attributes['TYPE']))\n\t\t{\n\t\t\tcase 'ENUM':\n\t\t\tcase 'SET':\n\t\t\t\t$attributes['TYPE'] = 'TEXT';\n\t\t\t\treturn;\n\t\t\tdefault: return;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute AUTO_INCREMENT\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_auto_increment(&$attributes, &$field)\n\t{\n\t\tif ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE)\n\t\t{\n\t\t\t$field['type'] = 'INTEGER PRIMARY KEY';\n\t\t\t$field['default'] = '';\n\t\t\t$field['null'] = '';\n\t\t\t$field['unique'] = '';\n\t\t\t$field['auto_increment'] = ' AUTOINCREMENT';\n\n\t\t\t$this->primary_keys = array();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/sqlite3/sqlite3_result.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * SQLite3 Result Class\n *\n * This class extends the parent result class: CI_DB_result\n *\n * @category\tDatabase\n * @author\t\tAndrey Andreev\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_sqlite3_result extends CI_DB_result {\n\n\t/**\n\t * Number of fields in the result set\n\t *\n\t * @return\tint\n\t */\n\tpublic function num_fields()\n\t{\n\t\treturn $this->result_id->numColumns();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch Field Names\n\t *\n\t * Generates an array of column names\n\t *\n\t * @return\tarray\n\t */\n\tpublic function list_fields()\n\t{\n\t\t$field_names = array();\n\t\tfor ($i = 0, $c = $this->num_fields(); $i < $c; $i++)\n\t\t{\n\t\t\t$field_names[] = $this->result_id->columnName($i);\n\t\t}\n\n\t\treturn $field_names;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field data\n\t *\n\t * Generates an array of objects containing field meta-data\n\t *\n\t * @return\tarray\n\t */\n\tpublic function field_data()\n\t{\n\t\tstatic $data_types = array(\n\t\t\tSQLITE3_INTEGER\t=> 'integer',\n\t\t\tSQLITE3_FLOAT\t=> 'float',\n\t\t\tSQLITE3_TEXT\t=> 'text',\n\t\t\tSQLITE3_BLOB\t=> 'blob',\n\t\t\tSQLITE3_NULL\t=> 'null'\n\t\t);\n\n\t\t$retval = array();\n\t\tfor ($i = 0, $c = $this->num_fields(); $i < $c; $i++)\n\t\t{\n\t\t\t$retval[$i]\t\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t\t= $this->result_id->columnName($i);\n\n\t\t\t$type = $this->result_id->columnType($i);\n\t\t\t$retval[$i]->type\t\t= isset($data_types[$type]) ? $data_types[$type] : $type;\n\n\t\t\t$retval[$i]->max_length\t\t= NULL;\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Free the result\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function free_result()\n\t{\n\t\tif (is_object($this->result_id))\n\t\t{\n\t\t\t$this->result_id->finalize();\n\t\t\t$this->result_id = NULL;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - associative array\n\t *\n\t * Returns the result set as an array\n\t *\n\t * @return\tarray\n\t */\n\tprotected function _fetch_assoc()\n\t{\n\t\treturn $this->result_id->fetchArray(SQLITE3_ASSOC);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - object\n\t *\n\t * Returns the result set as an object\n\t *\n\t * @param\tstring\t$class_name\n\t * @return\tobject\n\t */\n\tprotected function _fetch_object($class_name = 'stdClass')\n\t{\n\t\t// No native support for fetching rows as objects\n\t\tif (($row = $this->result_id->fetchArray(SQLITE3_ASSOC)) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\t\telseif ($class_name === 'stdClass')\n\t\t{\n\t\t\treturn (object) $row;\n\t\t}\n\n\t\t$class_name = new $class_name();\n\t\tforeach (array_keys($row) as $key)\n\t\t{\n\t\t\t$class_name->$key = $row[$key];\n\t\t}\n\n\t\treturn $class_name;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Data Seek\n\t *\n\t * Moves the internal pointer to the desired offset. We call\n\t * this internally before fetching results to make sure the\n\t * result set starts at zero.\n\t *\n\t * @param\tint\t$n\t(ignored)\n\t * @return\tarray\n\t */\n\tpublic function data_seek($n = 0)\n\t{\n\t\t// Only resetting to the start of the result set is supported\n\t\treturn ($n > 0) ? FALSE : $this->result_id->reset();\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/sqlite3/sqlite3_utility.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * SQLite3 Utility Class\n *\n * @category\tDatabase\n * @author\tAndrey Andreev\n * @link\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_sqlite3_utility extends CI_DB_utility {\n\n\t/**\n\t * Export\n\t *\n\t * @param\tarray\t$params\tPreferences\n\t * @return\tmixed\n\t */\n\tprotected function _backup($params = array())\n\t{\n\t\t// Not supported\n\t\treturn $this->db->display_error('db_unsupported_feature');\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/sqlsrv/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/database/drivers/sqlsrv/sqlsrv_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 2.0.3\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * SQLSRV Database Adapter Class\n *\n * Note: _DB is an extender class that the app controller\n * creates dynamically based on whether the query builder\n * class is being used or not.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tDrivers\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_sqlsrv_driver extends CI_DB {\n\n\t/**\n\t * Database driver\n\t *\n\t * @var\tstring\n\t */\n\tpublic $dbdriver = 'sqlsrv';\n\n\t/**\n\t * Scrollable flag\n\t *\n\t * Determines what cursor type to use when executing queries.\n\t *\n\t * FALSE or SQLSRV_CURSOR_FORWARD would increase performance,\n\t * but would disable num_rows() (and possibly insert_id())\n\t *\n\t * @var\tmixed\n\t */\n\tpublic $scrollable;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ORDER BY random keyword\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_random_keyword = array('NEWID()', 'RAND(%d)');\n\n\t/**\n\t * Quoted identifier flag\n\t *\n\t * Whether to use SQL-92 standard quoted identifier\n\t * (double quotes) or brackets for identifier escaping.\n\t *\n\t * @var\tbool\n\t */\n\tprotected $_quoted_identifier = TRUE;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * @param\tarray\t$params\n\t * @return\tvoid\n\t */\n\tpublic function __construct($params)\n\t{\n\t\tparent::__construct($params);\n\n\t\t// This is only supported as of SQLSRV 3.0\n\t\tif ($this->scrollable === NULL)\n\t\t{\n\t\t\t$this->scrollable = defined('SQLSRV_CURSOR_CLIENT_BUFFERED')\n\t\t\t\t? SQLSRV_CURSOR_CLIENT_BUFFERED\n\t\t\t\t: FALSE;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Database connection\n\t *\n\t * @param\tbool\t$pooling\n\t * @return\tresource\n\t */\n\tpublic function db_connect($pooling = FALSE)\n\t{\n\t\t$charset = in_array(strtolower($this->char_set), array('utf-8', 'utf8'), TRUE)\n\t\t\t? 'UTF-8' : SQLSRV_ENC_CHAR;\n\n\t\t$connection = array(\n\t\t\t'UID'\t\t\t=> empty($this->username) ? '' : $this->username,\n\t\t\t'PWD'\t\t\t=> empty($this->password) ? '' : $this->password,\n\t\t\t'Database'\t\t=> $this->database,\n\t\t\t'ConnectionPooling'\t=> ($pooling === TRUE) ? 1 : 0,\n\t\t\t'CharacterSet'\t\t=> $charset,\n\t\t\t'Encrypt'\t\t=> ($this->encrypt === TRUE) ? 1 : 0,\n\t\t\t'ReturnDatesAsStrings'\t=> 1\n\t\t);\n\n\t\t// If the username and password are both empty, assume this is a\n\t\t// 'Windows Authentication Mode' connection.\n\t\tif (empty($connection['UID']) && empty($connection['PWD']))\n\t\t{\n\t\t\tunset($connection['UID'], $connection['PWD']);\n\t\t}\n\n\t\tif (FALSE !== ($this->conn_id = sqlsrv_connect($this->hostname, $connection)))\n\t\t{\n\t\t\t// Determine how identifiers are escaped\n\t\t\t$query = $this->query('SELECT CASE WHEN (@@OPTIONS | 256) = @@OPTIONS THEN 1 ELSE 0 END AS qi');\n\t\t\t$query = $query->row_array();\n\t\t\t$this->_quoted_identifier = empty($query) ? FALSE : (bool) $query['qi'];\n\t\t\t$this->_escape_char = ($this->_quoted_identifier) ? '\"' : array('[', ']');\n\t\t}\n\n\t\treturn $this->conn_id;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Select the database\n\t *\n\t * @param\tstring\t$database\n\t * @return\tbool\n\t */\n\tpublic function db_select($database = '')\n\t{\n\t\tif ($database === '')\n\t\t{\n\t\t\t$database = $this->database;\n\t\t}\n\n\t\tif ($this->_execute('USE '.$this->escape_identifiers($database)))\n\t\t{\n\t\t\t$this->database = $database;\n\t\t\t$this->data_cache = array();\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Execute the query\n\t *\n\t * @param\tstring\t$sql\tan SQL query\n\t * @return\tresource\n\t */\n\tprotected function _execute($sql)\n\t{\n\t\treturn ($this->scrollable === FALSE OR $this->is_write_type($sql))\n\t\t\t? sqlsrv_query($this->conn_id, $sql)\n\t\t\t: sqlsrv_query($this->conn_id, $sql, NULL, array('Scrollable' => $this->scrollable));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Begin Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_begin()\n\t{\n\t\treturn sqlsrv_begin_transaction($this->conn_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Commit Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_commit()\n\t{\n\t\treturn sqlsrv_commit($this->conn_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Rollback Transaction\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _trans_rollback()\n\t{\n\t\treturn sqlsrv_rollback($this->conn_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Affected Rows\n\t *\n\t * @return\tint\n\t */\n\tpublic function affected_rows()\n\t{\n\t\treturn sqlsrv_rows_affected($this->result_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert ID\n\t *\n\t * Returns the last id created in the Identity column.\n\t *\n\t * @return\tstring\n\t */\n\tpublic function insert_id()\n\t{\n\t\treturn $this->query('SELECT SCOPE_IDENTITY() AS insert_id')->row()->insert_id;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Database version number\n\t *\n\t * @return\tstring\n\t */\n\tpublic function version()\n\t{\n\t\tif (isset($this->data_cache['version']))\n\t\t{\n\t\t\treturn $this->data_cache['version'];\n\t\t}\n\n\t\tif ( ! $this->conn_id OR ($info = sqlsrv_server_info($this->conn_id)) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn $this->data_cache['version'] = $info['SQLServerVersion'];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * List table query\n\t *\n\t * Generates a platform-specific query string so that the table names can be fetched\n\t *\n\t * @param\tbool\n\t * @return\tstring\t$prefix_limit\n\t */\n\tprotected function _list_tables($prefix_limit = FALSE)\n\t{\n\t\t$sql = 'SELECT '.$this->escape_identifiers('name')\n\t\t\t.' FROM '.$this->escape_identifiers('sysobjects')\n\t\t\t.' WHERE '.$this->escape_identifiers('type').\" = 'U'\";\n\n\t\tif ($prefix_limit === TRUE && $this->dbprefix !== '')\n\t\t{\n\t\t\t$sql .= ' AND '.$this->escape_identifiers('name').\" LIKE '\".$this->escape_like_str($this->dbprefix).\"%' \"\n\t\t\t\t.sprintf($this->_escape_like_str, $this->_escape_like_chr);\n\t\t}\n\n\t\treturn $sql.' ORDER BY '.$this->escape_identifiers('name');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * List column query\n\t *\n\t * Generates a platform-specific query string so that the column names can be fetched\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _list_columns($table = '')\n\t{\n\t\treturn 'SELECT COLUMN_NAME\n\t\t\tFROM INFORMATION_SCHEMA.Columns\n\t\t\tWHERE UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns an object with field data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tarray\n\t */\n\tpublic function field_data($table)\n\t{\n\t\t$sql = 'SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, COLUMN_DEFAULT\n\t\t\tFROM INFORMATION_SCHEMA.Columns\n\t\t\tWHERE UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));\n\n\t\tif (($query = $this->query($sql)) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\t\t$query = $query->result_object();\n\n\t\t$retval = array();\n\t\tfor ($i = 0, $c = count($query); $i < $c; $i++)\n\t\t{\n\t\t\t$retval[$i]\t\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t\t= $query[$i]->COLUMN_NAME;\n\t\t\t$retval[$i]->type\t\t= $query[$i]->DATA_TYPE;\n\t\t\t$retval[$i]->max_length\t\t= ($query[$i]->CHARACTER_MAXIMUM_LENGTH > 0) ? $query[$i]->CHARACTER_MAXIMUM_LENGTH : $query[$i]->NUMERIC_PRECISION;\n\t\t\t$retval[$i]->default\t\t= $query[$i]->COLUMN_DEFAULT;\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Error\n\t *\n\t * Returns an array containing code and message of the last\n\t * database error that has occurred.\n\t *\n\t * @return\tarray\n\t */\n\tpublic function error()\n\t{\n\t\t$error = array('code' => '00000', 'message' => '');\n\t\t$sqlsrv_errors = sqlsrv_errors(SQLSRV_ERR_ERRORS);\n\n\t\tif ( ! is_array($sqlsrv_errors))\n\t\t{\n\t\t\treturn $error;\n\t\t}\n\n\t\t$sqlsrv_error = array_shift($sqlsrv_errors);\n\t\tif (isset($sqlsrv_error['SQLSTATE']))\n\t\t{\n\t\t\t$error['code'] = isset($sqlsrv_error['code']) ? $sqlsrv_error['SQLSTATE'].'/'.$sqlsrv_error['code'] : $sqlsrv_error['SQLSTATE'];\n\t\t}\n\t\telseif (isset($sqlsrv_error['code']))\n\t\t{\n\t\t\t$error['code'] = $sqlsrv_error['code'];\n\t\t}\n\n\t\tif (isset($sqlsrv_error['message']))\n\t\t{\n\t\t\t$error['message'] = $sqlsrv_error['message'];\n\t\t}\n\n\t\treturn $error;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Update statement\n\t *\n\t * Generates a platform-specific update string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @param\tarray\t$values\n\t * @return\tstring\n\t */\n\tprotected function _update($table, $values)\n\t{\n\t\t$this->qb_limit = FALSE;\n\t\t$this->qb_orderby = array();\n\t\treturn parent::_update($table, $values);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Truncate statement\n\t *\n\t * Generates a platform-specific truncate string from the supplied data\n\t *\n\t * If the database does not support the TRUNCATE statement,\n\t * then this method maps to 'DELETE FROM table'\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _truncate($table)\n\t{\n\t\treturn 'TRUNCATE TABLE '.$table;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Delete statement\n\t *\n\t * Generates a platform-specific delete string from the supplied data\n\t *\n\t * @param\tstring\t$table\n\t * @return\tstring\n\t */\n\tprotected function _delete($table)\n\t{\n\t\tif ($this->qb_limit)\n\t\t{\n\t\t\treturn 'WITH ci_delete AS (SELECT TOP '.$this->qb_limit.' * FROM '.$table.$this->_compile_wh('qb_where').') DELETE FROM ci_delete';\n\t\t}\n\n\t\treturn parent::_delete($table);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * LIMIT\n\t *\n\t * Generates a platform-specific LIMIT clause\n\t *\n\t * @param\tstring\t$sql\tSQL Query\n\t * @return\tstring\n\t */\n\tprotected function _limit($sql)\n\t{\n\t\t// As of SQL Server 2012 (11.0.*) OFFSET is supported\n\t\tif (version_compare($this->version(), '11', '>='))\n\t\t{\n\t\t\t// SQL Server OFFSET-FETCH can be used only with the ORDER BY clause\n\t\t\tempty($this->qb_orderby) && $sql .= ' ORDER BY 1';\n\n\t\t\treturn $sql.' OFFSET '.(int) $this->qb_offset.' ROWS FETCH NEXT '.$this->qb_limit.' ROWS ONLY';\n\t\t}\n\n\t\t$limit = $this->qb_offset + $this->qb_limit;\n\n\t\t// An ORDER BY clause is required for ROW_NUMBER() to work\n\t\tif ($this->qb_offset && ! empty($this->qb_orderby))\n\t\t{\n\t\t\t$orderby = $this->_compile_order_by();\n\n\t\t\t// We have to strip the ORDER BY clause\n\t\t\t$sql = trim(substr($sql, 0, strrpos($sql, $orderby)));\n\n\t\t\t// Get the fields to select from our subquery, so that we can avoid CI_rownum appearing in the actual results\n\t\t\tif (count($this->qb_select) === 0 OR strpos(implode(',', $this->qb_select), '*') !== FALSE)\n\t\t\t{\n\t\t\t\t$select = '*'; // Inevitable\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Use only field names and their aliases, everything else is out of our scope.\n\t\t\t\t$select = array();\n\t\t\t\t$field_regexp = ($this->_quoted_identifier)\n\t\t\t\t\t? '(\"[^\\\"]+\")' : '(\\[[^\\]]+\\])';\n\t\t\t\tfor ($i = 0, $c = count($this->qb_select); $i < $c; $i++)\n\t\t\t\t{\n\t\t\t\t\t$select[] = preg_match('/(?:\\s|\\.)'.$field_regexp.'$/i', $this->qb_select[$i], $m)\n\t\t\t\t\t\t? $m[1] : $this->qb_select[$i];\n\t\t\t\t}\n\t\t\t\t$select = implode(', ', $select);\n\t\t\t}\n\n\t\t\treturn 'SELECT '.$select.\" FROM (\\n\\n\"\n\t\t\t\t.preg_replace('/^(SELECT( DISTINCT)?)/i', '\\\\1 ROW_NUMBER() OVER('.trim($orderby).') AS '.$this->escape_identifiers('CI_rownum').', ', $sql)\n\t\t\t\t.\"\\n\\n) \".$this->escape_identifiers('CI_subquery')\n\t\t\t\t.\"\\nWHERE \".$this->escape_identifiers('CI_rownum').' BETWEEN '.($this->qb_offset + 1).' AND '.$limit;\n\t\t}\n\n\t\treturn preg_replace('/(^\\SELECT (DISTINCT)?)/i','\\\\1 TOP '.$limit.' ', $sql);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Insert batch statement\n\t *\n\t * Generates a platform-specific insert string from the supplied data.\n\t *\n\t * @param\tstring\t$table\tTable name\n\t * @param\tarray\t$keys\tINSERT keys\n\t * @param\tarray\t$values\tINSERT values\n\t * @return\tstring|bool\n\t */\n\tprotected function _insert_batch($table, $keys, $values)\n\t{\n\t\t// Multiple-value inserts are only supported as of SQL Server 2008\n\t\tif (version_compare($this->version(), '10', '>='))\n\t\t{\n\t\t\treturn parent::_insert_batch($table, $keys, $values);\n\t\t}\n\n\t\treturn ($this->db_debug) ? $this->display_error('db_unsupported_feature') : FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Close DB Connection\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _close()\n\t{\n\t\tsqlsrv_close($this->conn_id);\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/sqlsrv/sqlsrv_forge.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 2.0.3\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * SQLSRV Forge Class\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_sqlsrv_forge extends CI_DB_forge {\n\n\t/**\n\t * CREATE TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_create_table_if\t= \"IF NOT EXISTS (SELECT * FROM sysobjects WHERE ID = object_id(N'%s') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)\\nCREATE TABLE\";\n\n\t/**\n\t * DROP TABLE IF statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_drop_table_if\t= \"IF EXISTS (SELECT * FROM sysobjects WHERE ID = object_id(N'%s') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)\\nDROP TABLE\";\n\n\t/**\n\t * UNSIGNED support\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_unsigned\t\t= array(\n\t\t'TINYINT'\t=> 'SMALLINT',\n\t\t'SMALLINT'\t=> 'INT',\n\t\t'INT'\t\t=> 'BIGINT',\n\t\t'REAL'\t\t=> 'FLOAT'\n\t);\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ALTER TABLE\n\t *\n\t * @param\tstring\t$alter_type\tALTER type\n\t * @param\tstring\t$table\t\tTable name\n\t * @param\tmixed\t$field\t\tColumn definition\n\t * @return\tstring|string[]\n\t */\n\tprotected function _alter_table($alter_type, $table, $field)\n\t{\n\t\tif (in_array($alter_type, array('ADD', 'DROP'), TRUE))\n\t\t{\n\t\t\treturn parent::_alter_table($alter_type, $table, $field);\n\t\t}\n\n\t\t$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' ALTER COLUMN ';\n\t\t$sqls = array();\n\t\tfor ($i = 0, $c = count($field); $i < $c; $i++)\n\t\t{\n\t\t\t$sqls[] = $sql.$this->_process_column($field[$i]);\n\t\t}\n\n\t\treturn $sqls;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute TYPE\n\t *\n\t * Performs a data type mapping between different databases.\n\t *\n\t * @param\tarray\t&$attributes\n\t * @return\tvoid\n\t */\n\tprotected function _attr_type(&$attributes)\n\t{\n\t\tif (isset($attributes['CONSTRAINT']) && strpos($attributes['TYPE'], 'INT') !== FALSE)\n\t\t{\n\t\t\tunset($attributes['CONSTRAINT']);\n\t\t}\n\n\t\tswitch (strtoupper($attributes['TYPE']))\n\t\t{\n\t\t\tcase 'MEDIUMINT':\n\t\t\t\t$attributes['TYPE'] = 'INTEGER';\n\t\t\t\t$attributes['UNSIGNED'] = FALSE;\n\t\t\t\treturn;\n\t\t\tcase 'INTEGER':\n\t\t\t\t$attributes['TYPE'] = 'INT';\n\t\t\t\treturn;\n\t\t\tdefault: return;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field attribute AUTO_INCREMENT\n\t *\n\t * @param\tarray\t&$attributes\n\t * @param\tarray\t&$field\n\t * @return\tvoid\n\t */\n\tprotected function _attr_auto_increment(&$attributes, &$field)\n\t{\n\t\tif ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE)\n\t\t{\n\t\t\t$field['auto_increment'] = ' IDENTITY(1,1)';\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/sqlsrv/sqlsrv_result.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 2.0.3\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * SQLSRV Result Class\n *\n * This class extends the parent result class: CI_DB_result\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_sqlsrv_result extends CI_DB_result {\n\n\t/**\n\t * Scrollable flag\n\t *\n\t * @var\tmixed\n\t */\n\tpublic $scrollable;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Constructor\n\t *\n\t * @param\tobject\t$driver_object\n\t * @return\tvoid\n\t */\n\tpublic function __construct(&$driver_object)\n\t{\n\t\tparent::__construct($driver_object);\n\n\t\t$this->scrollable = $driver_object->scrollable;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Number of rows in the result set\n\t *\n\t * @return\tint\n\t */\n\tpublic function num_rows()\n\t{\n\t\t// sqlsrv_num_rows() doesn't work with the FORWARD and DYNAMIC cursors (FALSE is the same as FORWARD)\n\t\tif ( ! in_array($this->scrollable, array(FALSE, SQLSRV_CURSOR_FORWARD, SQLSRV_CURSOR_DYNAMIC), TRUE))\n\t\t{\n\t\t\treturn parent::num_rows();\n\t\t}\n\n\t\treturn is_int($this->num_rows)\n\t\t\t? $this->num_rows\n\t\t\t: $this->num_rows = sqlsrv_num_rows($this->result_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Number of fields in the result set\n\t *\n\t * @return\tint\n\t */\n\tpublic function num_fields()\n\t{\n\t\treturn @sqlsrv_num_fields($this->result_id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch Field Names\n\t *\n\t * Generates an array of column names\n\t *\n\t * @return\tarray\n\t */\n\tpublic function list_fields()\n\t{\n\t\t$field_names = array();\n\t\tforeach (sqlsrv_field_metadata($this->result_id) as $offset => $field)\n\t\t{\n\t\t\t$field_names[] = $field['Name'];\n\t\t}\n\n\t\treturn $field_names;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Field data\n\t *\n\t * Generates an array of objects containing field meta-data\n\t *\n\t * @return\tarray\n\t */\n\tpublic function field_data()\n\t{\n\t\t$retval = array();\n\t\tforeach (sqlsrv_field_metadata($this->result_id) as $i => $field)\n\t\t{\n\t\t\t$retval[$i]\t\t= new stdClass();\n\t\t\t$retval[$i]->name\t= $field['Name'];\n\t\t\t$retval[$i]->type\t= $field['Type'];\n\t\t\t$retval[$i]->max_length\t= $field['Size'];\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Free the result\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function free_result()\n\t{\n\t\tif (is_resource($this->result_id))\n\t\t{\n\t\t\tsqlsrv_free_stmt($this->result_id);\n\t\t\t$this->result_id = FALSE;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - associative array\n\t *\n\t * Returns the result set as an array\n\t *\n\t * @return\tarray\n\t */\n\tprotected function _fetch_assoc()\n\t{\n\t\treturn sqlsrv_fetch_array($this->result_id, SQLSRV_FETCH_ASSOC);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result - object\n\t *\n\t * Returns the result set as an object\n\t *\n\t * @param\tstring\t$class_name\n\t * @return\tobject\n\t */\n\tprotected function _fetch_object($class_name = 'stdClass')\n\t{\n\t\treturn sqlsrv_fetch_object($this->result_id, $class_name);\n\t}\n\n}\n"
  },
  {
    "path": "system/database/drivers/sqlsrv/sqlsrv_utility.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 2.0.3\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * SQLSRV Utility Class\n *\n * @category\tDatabase\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/database/\n */\nclass CI_DB_sqlsrv_utility extends CI_DB_utility {\n\n\t/**\n\t * List databases statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_list_databases\t= 'EXEC sp_helpdb'; // Can also be: EXEC sp_databases\n\n\t/**\n\t * OPTIMIZE TABLE statement\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_optimize_table\t= 'ALTER INDEX all ON %s REORGANIZE';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Export\n\t *\n\t * @param\tarray\t$params\tPreferences\n\t * @return\tbool\n\t */\n\tprotected function _backup($params = array())\n\t{\n\t\t// Currently unsupported\n\t\treturn $this->db->display_error('db_unsupported_feature');\n\t}\n\n}\n"
  },
  {
    "path": "system/database/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/fonts/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/helpers/array_helper.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Array Helpers\n *\n * @package\t\tCodeIgniter\n * @subpackage\tHelpers\n * @category\tHelpers\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/helpers/array_helper.html\n */\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('element'))\n{\n\t/**\n\t * Element\n\t *\n\t * Lets you determine whether an array index is set and whether it has a value.\n\t * If the element is empty it returns NULL (or whatever you specify as the default value.)\n\t *\n\t * @param\tstring\n\t * @param\tarray\n\t * @param\tmixed\n\t * @return\tmixed\tdepends on what the array contains\n\t */\n\tfunction element($item, array $array, $default = NULL)\n\t{\n\t\treturn array_key_exists($item, $array) ? $array[$item] : $default;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('random_element'))\n{\n\t/**\n\t * Random Element - Takes an array as input and returns a random element\n\t *\n\t * @param\tarray\n\t * @return\tmixed\tdepends on what the array contains\n\t */\n\tfunction random_element($array)\n\t{\n\t\treturn is_array($array) ? $array[array_rand($array)] : $array;\n\t}\n}\n\n// --------------------------------------------------------------------\n\nif ( ! function_exists('elements'))\n{\n\t/**\n\t * Elements\n\t *\n\t * Returns only the array items specified. Will return a default value if\n\t * it is not set.\n\t *\n\t * @param\tarray\n\t * @param\tarray\n\t * @param\tmixed\n\t * @return\tmixed\tdepends on what the array contains\n\t */\n\tfunction elements($items, array $array, $default = NULL)\n\t{\n\t\t$return = array();\n\n\t\tis_array($items) OR $items = array($items);\n\n\t\tforeach ($items as $item)\n\t\t{\n\t\t\t$return[$item] = array_key_exists($item, $array) ? $array[$item] : $default;\n\t\t}\n\n\t\treturn $return;\n\t}\n}\n"
  },
  {
    "path": "system/helpers/captcha_helper.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter CAPTCHA Helper\n *\n * @package\t\tCodeIgniter\n * @subpackage\tHelpers\n * @category\tHelpers\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/helpers/captcha_helper.html\n */\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('create_captcha'))\n{\n\t/**\n\t * Create CAPTCHA\n\t *\n\t * @param\tarray\t$data\tData for the CAPTCHA\n\t * @return\tarray\n\t */\n\tfunction create_captcha($data)\n\t{\n\t\t$defaults = array(\n\t\t\t'word'\t\t=> '',\n\t\t\t'img_path'\t=> '',\n\t\t\t'img_url'\t=> '',\n\t\t\t'img_width'\t=> '150',\n\t\t\t'img_height'\t=> '30',\n\t\t\t'img_alt'\t=> 'captcha',\n\t\t\t'img_class'\t=> '',\n\t\t\t'font_path'\t=> '',\n\t\t\t'font_size'\t=> 16,\n\t\t\t'expiration'\t=> 7200,\n\t\t\t'word_length'\t=> 8,\n\t\t\t'img_id'\t=> '',\n\t\t\t'pool'\t\t=> '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',\n\t\t\t'colors'\t=> array(\n\t\t\t\t'background'\t=> array(255,255,255),\n\t\t\t\t'border'\t=> array(153,102,102),\n\t\t\t\t'text'\t\t=> array(204,153,153),\n\t\t\t\t'grid'\t\t=> array(255,182,182)\n\t\t\t)\n\t\t);\n\n\t\t$now = microtime(TRUE);\n\n\t\tforeach ($defaults as $key => $val)\n\t\t{\n\t\t\tif ( ! is_array($data) && empty($$key))\n\t\t\t{\n\t\t\t\t$$key = $val;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$$key = isset($data[$key]) ? $data[$key] : $val;\n\t\t\t}\n\t\t}\n\n\t\tif ( ! extension_loaded('gd'))\n\t\t{\n\t\t\tlog_message('error', 'create_captcha(): GD extension is not loaded.');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif ($img_path === '' OR $img_url === '')\n\t\t{\n\t\t\tlog_message('error', 'create_captcha(): img_path and img_url are required.');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif ( ! is_dir($img_path) OR ! is_really_writable($img_path))\n\t\t{\n\t\t\tlog_message('error', \"create_captcha(): '{$img_path}' is not a dir, nor is it writable.\");\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif ($img_url !== '' OR $img_path !== '')\n\t\t{\n\t\t\tif ($img_path === '' OR $img_url === '')\n\t\t\t{\n\t\t\t\tlog_message('error', 'create_captcha(): $img_path and $img_url are required.');\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\tif ( ! is_dir($img_path) OR ! is_really_writable($img_path))\n\t\t\t{\n\t\t\t\tlog_message('error', \"create_captcha(): '{$img_path}' is not a dir, nor is it writable.\");\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Remove old images\n\t\t\t */\n\t\t\t$current_dir = @opendir($img_path);\n\t\t\twhile ($filename = @readdir($current_dir))\n\t\t\t{\n\t\t\t\tif (preg_match('#^(?<ts>\\d{10})\\.png$#', $filename, $match) && ($match['ts'] + $expiration) < $now)\n\t\t\t\t{\n\t\t\t\t\t@unlink($img_path.$filename);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@closedir($current_dir);\n\n\t\t\t// This variable will later be used later to determine whether we write to disk or output a data:image URI\n\t\t\t$img_filename = $now.'.png';\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$img_filename = NULL;\n\t\t}\n\n\t\t// -----------------------------------\n\t\t// Do we have a \"word\" yet?\n\t\t// -----------------------------------\n\n\t\tif (empty($word))\n\t\t{\n\t\t\t$word = '';\n\t\t\t$pool_length = strlen($pool);\n\t\t\t$rand_max = $pool_length - 1;\n\n\t\t\t// PHP7 or a suitable polyfill\n\t\t\tif (function_exists('random_int'))\n\t\t\t{\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tfor ($i = 0; $i < $word_length; $i++)\n\t\t\t\t\t{\n\t\t\t\t\t\t$word .= $pool[random_int(0, $rand_max)];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception $e)\n\t\t\t\t{\n\t\t\t\t\t// This means fallback to the next possible\n\t\t\t\t\t// alternative to random_int()\n\t\t\t\t\t$word = '';\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (empty($word))\n\t\t{\n\t\t\t// Nobody will have a larger character pool than\n\t\t\t// 256 characters, but let's handle it just in case ...\n\t\t\t//\n\t\t\t// No, I do not care that the fallback to mt_rand() can\n\t\t\t// handle it; if you trigger this, you're very obviously\n\t\t\t// trying to break it. -- Narf\n\t\t\tif ($pool_length > 256)\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\t// We'll try using the operating system's PRNG first,\n\t\t\t// which we can access through CI_Security::get_random_bytes()\n\t\t\t$security = get_instance()->security;\n\n\t\t\t// To avoid numerous get_random_bytes() calls, we'll\n\t\t\t// just try fetching as much bytes as we need at once.\n\t\t\tif (($bytes = $security->get_random_bytes($pool_length)) !== FALSE)\n\t\t\t{\n\t\t\t\t$byte_index = $word_index = 0;\n\t\t\t\twhile ($word_index < $word_length)\n\t\t\t\t{\n\t\t\t\t\t// Do we have more random data to use?\n\t\t\t\t\t// It could be exhausted by previous iterations\n\t\t\t\t\t// ignoring bytes higher than $rand_max.\n\t\t\t\t\tif ($byte_index === $pool_length)\n\t\t\t\t\t{\n\t\t\t\t\t\t// No failures should be possible if the\n\t\t\t\t\t\t// first get_random_bytes() call didn't\n\t\t\t\t\t\t// return FALSE, but still ...\n\t\t\t\t\t\tfor ($i = 0; $i < 5; $i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (($bytes = $security->get_random_bytes($pool_length)) === FALSE)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t$byte_index = 0;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ($bytes === FALSE)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// Sadly, this means fallback to mt_rand()\n\t\t\t\t\t\t\t$word = '';\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tlist(, $rand_index) = unpack('C', $bytes[$byte_index++]);\n\t\t\t\t\tif ($rand_index > $rand_max)\n\t\t\t\t\t{\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t$word .= $pool[$rand_index];\n\t\t\t\t\t$word_index++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (empty($word))\n\t\t{\n\t\t\tfor ($i = 0; $i < $word_length; $i++)\n\t\t\t{\n\t\t\t\t$word .= $pool[mt_rand(0, $rand_max)];\n\t\t\t}\n\t\t}\n\t\telseif ( ! is_string($word))\n\t\t{\n\t\t\t$word = (string) $word;\n\t\t}\n\n\t\t// -----------------------------------\n\t\t// Determine angle and position\n\t\t// -----------------------------------\n\t\t$length\t= strlen($word);\n\t\t$angle\t= ($length >= 6) ? mt_rand(-($length - 6), ($length - 6)) : 0;\n\t\t$x_axis\t= mt_rand(6, (360 / $length)-16);\n\t\t$y_axis = ($angle >= 0) ? mt_rand($img_height, $img_width) : mt_rand(6, $img_height);\n\n\t\t// Create image\n\t\t// PHP.net recommends imagecreatetruecolor(), but it isn't always available\n\t\t$im = function_exists('imagecreatetruecolor')\n\t\t\t? imagecreatetruecolor($img_width, $img_height)\n\t\t\t: imagecreate($img_width, $img_height);\n\n\t\t// -----------------------------------\n\t\t//  Assign colors\n\t\t// ----------------------------------\n\n\t\tis_array($colors) OR $colors = $defaults['colors'];\n\n\t\tforeach (array_keys($defaults['colors']) as $key)\n\t\t{\n\t\t\t// Check for a possible missing value\n\t\t\tis_array($colors[$key]) OR $colors[$key] = $defaults['colors'][$key];\n\t\t\t$colors[$key] = imagecolorallocate($im, $colors[$key][0], $colors[$key][1], $colors[$key][2]);\n\t\t}\n\n\t\t// Create the rectangle\n\t\tImageFilledRectangle($im, 0, 0, $img_width, $img_height, $colors['background']);\n\n\t\t// -----------------------------------\n\t\t//  Create the spiral pattern\n\t\t// -----------------------------------\n\t\t$theta\t\t= 1;\n\t\t$thetac\t\t= 7;\n\t\t$radius\t\t= 16;\n\t\t$circles\t= 20;\n\t\t$points\t\t= 32;\n\n\t\tfor ($i = 0, $cp = ($circles * $points) - 1; $i < $cp; $i++)\n\t\t{\n\t\t\t$theta += $thetac;\n\t\t\t$rad = $radius * ($i / $points);\n\t\t\t$x = round(($rad * cos($theta)) + $x_axis);\n\t\t\t$y = round(($rad * sin($theta)) + $y_axis);\n\t\t\t$theta += $thetac;\n\t\t\t$rad1 = $radius * (($i + 1) / $points);\n\t\t\t$x1 = round(($rad1 * cos($theta)) + $x_axis);\n\t\t\t$y1 = round(($rad1 * sin($theta)) + $y_axis);\n\t\t\timageline($im, $x, $y, $x1, $y1, $colors['grid']);\n\t\t\t$theta -= $thetac;\n\t\t}\n\n\t\t// -----------------------------------\n\t\t//  Write the text\n\t\t// -----------------------------------\n\n\t\t$use_font = ($font_path !== '' && file_exists($font_path) && function_exists('imagettftext'));\n\t\tif ($use_font === FALSE)\n\t\t{\n\t\t\t($font_size > 5) && $font_size = 5;\n\t\t\t$x = mt_rand(0, $img_width / ($length / 3));\n\t\t\t$y = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t($font_size > 30) && $font_size = 30;\n\t\t\t$x = mt_rand(0, $img_width / ($length / 1.5));\n\t\t\t$y = $font_size + 2;\n\t\t}\n\n\t\tfor ($i = 0; $i < $length; $i++)\n\t\t{\n\t\t\tif ($use_font === FALSE)\n\t\t\t{\n\t\t\t\t$y = mt_rand(0 , $img_height / 2);\n\t\t\t\timagestring($im, $font_size, $x, $y, $word[$i], $colors['text']);\n\t\t\t\t$x += ($font_size * 2);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$y = mt_rand($img_height / 2, $img_height - 3);\n\t\t\t\timagettftext($im, $font_size, $angle, $x, $y, $colors['text'], $font_path, $word[$i]);\n\t\t\t\t$x += $font_size;\n\t\t\t}\n\t\t}\n\n\t\t// Create the border\n\t\timagerectangle($im, 0, 0, $img_width - 1, $img_height - 1, $colors['border']);\n\n\t\t// -----------------------------------\n\t\t//  Generate the image\n\t\t// -----------------------------------\n\n\t\tif (isset($img_filename))\n\t\t{\n\t\t\t$img_src = rtrim($img_url, '/').'/'.$img_filename;\n\t\t\timagepng($im, $img_path.$img_filename);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// I don't see an easier way to get the image contents without writing to file\n\t\t\t$buffer = fopen('php://memory', 'wb+');\n\t\t\timagepng($im, $buffer);\n\t\t\trewind($buffer);\n\t\t\t$img_src = '';\n\n\t\t\t// fread() will return an empty string (not FALSE) after the entire contents are read\n\t\t\twhile (strlen($read = fread($buffer, 4096)))\n\t\t\t{\n\t\t\t\t$img_src .= $read;\n\t\t\t}\n\n\t\t\tfclose($buffer);\n\t\t\t$img_src = 'data:image/png;base64,'.base64_encode($img_src);\n\t\t}\n\n\t\t$img_class = trim($img_class);\n\t\t$img_class = (bool) strlen($img_class) ? 'class=\"'.$img_class.'\" ' : '';\n\n\t\t$img = '<img '.($img_id === '' ? '' : 'id=\"'.$img_id.'\"').' src=\"'.$img_src.'\" style=\"width: '.$img_width.'px; height: '.$img_height .'px; border: 0;\" '.$img_class.'alt=\"'.$img_alt.'\" />';\n\t\tImageDestroy($im);\n\n\t\treturn array('word' => $word, 'time' => $now, 'image' => $img, 'filename' => $img_filename);\n\t}\n}\n"
  },
  {
    "path": "system/helpers/cookie_helper.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Cookie Helpers\n *\n * @package\t\tCodeIgniter\n * @subpackage\tHelpers\n * @category\tHelpers\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/helpers/cookie_helper.html\n */\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('set_cookie'))\n{\n\t/**\n\t * Set cookie\n\t *\n\t * Accepts seven parameters, or you can submit an associative\n\t * array in the first parameter containing all the values.\n\t *\n\t * @param\tmixed\n\t * @param\tstring\tthe value of the cookie\n\t * @param\tint\tthe number of seconds until expiration\n\t * @param\tstring\tthe cookie domain.  Usually:  .yourdomain.com\n\t * @param\tstring\tthe cookie path\n\t * @param\tstring\tthe cookie prefix\n\t * @param\tbool\ttrue makes the cookie secure\n\t * @param\tbool\ttrue makes the cookie accessible via http(s) only (no javascript)\n\t * @return\tvoid\n\t */\n\tfunction set_cookie($name, $value = '', $expire = 0, $domain = '', $path = '/', $prefix = '', $secure = NULL, $httponly = NULL, $samesite = NULL)\n\t{\n\t\t// Set the config file options\n\t\tget_instance()->input->set_cookie($name, $value, $expire, $domain, $path, $prefix, $secure, $httponly, $samesite);\n\t}\n}\n\n// --------------------------------------------------------------------\n\nif ( ! function_exists('get_cookie'))\n{\n\t/**\n\t * Fetch an item from the COOKIE array\n\t *\n\t * @param\tstring\n\t * @param\tbool\n\t * @return\tmixed\n\t */\n\tfunction get_cookie($index, $xss_clean = FALSE)\n\t{\n\t\t$prefix = isset($_COOKIE[$index]) ? '' : config_item('cookie_prefix');\n\t\treturn get_instance()->input->cookie($prefix.$index, $xss_clean);\n\t}\n}\n\n// --------------------------------------------------------------------\n\nif ( ! function_exists('delete_cookie'))\n{\n\t/**\n\t * Delete a COOKIE\n\t *\n\t * @param\tmixed\n\t * @param\tstring\tthe cookie domain. Usually: .yourdomain.com\n\t * @param\tstring\tthe cookie path\n\t * @param\tstring\tthe cookie prefix\n\t * @return\tvoid\n\t */\n\tfunction delete_cookie($name, $domain = '', $path = '/', $prefix = '')\n\t{\n\t\tset_cookie($name, '', '', $domain, $path, $prefix);\n\t}\n}\n"
  },
  {
    "path": "system/helpers/date_helper.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Date Helpers\n *\n * @package\t\tCodeIgniter\n * @subpackage\tHelpers\n * @category\tHelpers\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/helpers/date_helper.html\n */\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('now'))\n{\n\t/**\n\t * Get \"now\" time\n\t *\n\t * Returns time() based on the timezone parameter or on the\n\t * \"time_reference\" setting\n\t *\n\t * @param\tstring\n\t * @return\tint\n\t */\n\tfunction now($timezone = NULL)\n\t{\n\t\tif (empty($timezone))\n\t\t{\n\t\t\t$timezone = config_item('time_reference');\n\t\t}\n\n\t\tif ($timezone === 'local' OR $timezone === date_default_timezone_get())\n\t\t{\n\t\t\treturn time();\n\t\t}\n\n\t\t$datetime = new DateTime('now', new DateTimeZone($timezone));\n\t\tsscanf($datetime->format('j-n-Y G:i:s'), '%d-%d-%d %d:%d:%d', $day, $month, $year, $hour, $minute, $second);\n\n\t\treturn mktime($hour, $minute, $second, $month, $day, $year);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('mdate'))\n{\n\t/**\n\t * Convert MySQL Style Datecodes\n\t *\n\t * This function is identical to PHPs date() function,\n\t * except that it allows date codes to be formatted using\n\t * the MySQL style, where each code letter is preceded\n\t * with a percent sign:  %Y %m %d etc...\n\t *\n\t * The benefit of doing dates this way is that you don't\n\t * have to worry about escaping your text letters that\n\t * match the date codes.\n\t *\n\t * @param\tstring\n\t * @param\tint\n\t * @return\tint\n\t */\n\tfunction mdate($datestr = '', $time = '')\n\t{\n\t\tif ($datestr === '')\n\t\t{\n\t\t\treturn '';\n\t\t}\n\t\telseif (empty($time))\n\t\t{\n\t\t\t$time = now();\n\t\t}\n\n\t\t$datestr = str_replace(\n\t\t\t'%\\\\',\n\t\t\t'',\n\t\t\tpreg_replace('/([a-z]+?){1}/i', '\\\\\\\\\\\\1', $datestr)\n\t\t);\n\n\t\treturn date($datestr, $time);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('timespan'))\n{\n\t/**\n\t * Timespan\n\t *\n\t * Returns a span of seconds in this format:\n\t *\t10 days 14 hours 36 minutes 47 seconds\n\t *\n\t * @param\tint\ta number of seconds\n\t * @param\tint\tUnix timestamp\n\t * @param\tint\ta number of display units\n\t * @return\tstring\n\t */\n\tfunction timespan($seconds = 1, $time = '', $units = 7)\n\t{\n\t\t$CI =& get_instance();\n\t\t$CI->lang->load('date');\n\n\t\tis_numeric($seconds) OR $seconds = 1;\n\t\tis_numeric($time) OR $time = time();\n\t\tis_numeric($units) OR $units = 7;\n\n\t\t$seconds = ($time <= $seconds) ? 1 : $time - $seconds;\n\n\t\t$str = array();\n\t\t$years = floor($seconds / 31557600);\n\n\t\tif ($years > 0)\n\t\t{\n\t\t\t$str[] = $years.' '.$CI->lang->line($years > 1 ? 'date_years' : 'date_year');\n\t\t}\n\n\t\t$seconds -= $years * 31557600;\n\t\t$months = floor($seconds / 2629743);\n\n\t\tif (count($str) < $units && ($years > 0 OR $months > 0))\n\t\t{\n\t\t\tif ($months > 0)\n\t\t\t{\n\t\t\t\t$str[] = $months.' '.$CI->lang->line($months > 1 ? 'date_months' : 'date_month');\n\t\t\t}\n\n\t\t\t$seconds -= $months * 2629743;\n\t\t}\n\n\t\t$weeks = floor($seconds / 604800);\n\n\t\tif (count($str) < $units && ($years > 0 OR $months > 0 OR $weeks > 0))\n\t\t{\n\t\t\tif ($weeks > 0)\n\t\t\t{\n\t\t\t\t$str[] = $weeks.' '.$CI->lang->line($weeks > 1 ? 'date_weeks' : 'date_week');\n\t\t\t}\n\n\t\t\t$seconds -= $weeks * 604800;\n\t\t}\n\n\t\t$days = floor($seconds / 86400);\n\n\t\tif (count($str) < $units && ($months > 0 OR $weeks > 0 OR $days > 0))\n\t\t{\n\t\t\tif ($days > 0)\n\t\t\t{\n\t\t\t\t$str[] = $days.' '.$CI->lang->line($days > 1 ? 'date_days' : 'date_day');\n\t\t\t}\n\n\t\t\t$seconds -= $days * 86400;\n\t\t}\n\n\t\t$hours = floor($seconds / 3600);\n\n\t\tif (count($str) < $units && ($days > 0 OR $hours > 0))\n\t\t{\n\t\t\tif ($hours > 0)\n\t\t\t{\n\t\t\t\t$str[] = $hours.' '.$CI->lang->line($hours > 1 ? 'date_hours' : 'date_hour');\n\t\t\t}\n\n\t\t\t$seconds -= $hours * 3600;\n\t\t}\n\n\t\t$minutes = floor($seconds / 60);\n\n\t\tif (count($str) < $units && ($days > 0 OR $hours > 0 OR $minutes > 0))\n\t\t{\n\t\t\tif ($minutes > 0)\n\t\t\t{\n\t\t\t\t$str[] = $minutes.' '.$CI->lang->line($minutes > 1 ? 'date_minutes' : 'date_minute');\n\t\t\t}\n\n\t\t\t$seconds -= $minutes * 60;\n\t\t}\n\n\t\tif (count($str) === 0)\n\t\t{\n\t\t\t$str[] = $seconds.' '.$CI->lang->line($seconds > 1 ? 'date_seconds' : 'date_second');\n\t\t}\n\n\t\treturn implode(', ', $str);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('days_in_month'))\n{\n\t/**\n\t * Number of days in a month\n\t *\n\t * Takes a month/year as input and returns the number of days\n\t * for the given month/year. Takes leap years into consideration.\n\t *\n\t * @param\tint\ta numeric month\n\t * @param\tint\ta numeric year\n\t * @return\tint\n\t */\n\tfunction days_in_month($month = 0, $year = '')\n\t{\n\t\tif ($month < 1 OR $month > 12)\n\t\t{\n\t\t\treturn 0;\n\t\t}\n\t\telseif ( ! is_numeric($year) OR strlen($year) !== 4)\n\t\t{\n\t\t\t$year = date('Y');\n\t\t}\n\n\t\tif (defined('CAL_GREGORIAN'))\n\t\t{\n\t\t\treturn cal_days_in_month(CAL_GREGORIAN, $month, $year);\n\t\t}\n\n\t\tif ($year >= 1970)\n\t\t{\n\t\t\treturn (int) date('t', mktime(12, 0, 0, $month, 1, $year));\n\t\t}\n\n\t\tif ($month == 2)\n\t\t{\n\t\t\tif ($year % 400 === 0 OR ($year % 4 === 0 && $year % 100 !== 0))\n\t\t\t{\n\t\t\t\treturn 29;\n\t\t\t}\n\t\t}\n\n\t\t$days_in_month\t= array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);\n\t\treturn $days_in_month[$month - 1];\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('local_to_gmt'))\n{\n\t/**\n\t * Converts a local Unix timestamp to GMT\n\t *\n\t * @param\tint\tUnix timestamp\n\t * @return\tint\n\t */\n\tfunction local_to_gmt($time = '')\n\t{\n\t\tif ($time === '')\n\t\t{\n\t\t\t$time = time();\n\t\t}\n\n\t\treturn mktime(\n\t\t\tgmdate('G', $time),\n\t\t\tgmdate('i', $time),\n\t\t\tgmdate('s', $time),\n\t\t\tgmdate('n', $time),\n\t\t\tgmdate('j', $time),\n\t\t\tgmdate('Y', $time)\n\t\t);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('gmt_to_local'))\n{\n\t/**\n\t * Converts GMT time to a localized value\n\t *\n\t * Takes a Unix timestamp (in GMT) as input, and returns\n\t * at the local value based on the timezone and DST setting\n\t * submitted\n\t *\n\t * @param\tint\tUnix timestamp\n\t * @param\tstring\ttimezone\n\t * @param\tbool\twhether DST is active\n\t * @return\tint\n\t */\n\tfunction gmt_to_local($time = '', $timezone = 'UTC', $dst = FALSE)\n\t{\n\t\tif ($time === '')\n\t\t{\n\t\t\treturn now();\n\t\t}\n\n\t\t$time += timezones($timezone) * 3600;\n\n\t\treturn ($dst === TRUE) ? $time + 3600 : $time;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('mysql_to_unix'))\n{\n\t/**\n\t * Converts a MySQL Timestamp to Unix\n\t *\n\t * @param\tint\tMySQL timestamp YYYY-MM-DD HH:MM:SS\n\t * @return\tint\tUnix timstamp\n\t */\n\tfunction mysql_to_unix($time = '')\n\t{\n\t\t// We'll remove certain characters for backward compatibility\n\t\t// since the formatting changed with MySQL 4.1\n\t\t// YYYY-MM-DD HH:MM:SS\n\n\t\t$time = str_replace(array('-', ':', ' '), '', $time);\n\n\t\t// YYYYMMDDHHMMSS\n\t\treturn mktime(\n\t\t\tsubstr($time, 8, 2),\n\t\t\tsubstr($time, 10, 2),\n\t\t\tsubstr($time, 12, 2),\n\t\t\tsubstr($time, 4, 2),\n\t\t\tsubstr($time, 6, 2),\n\t\t\tsubstr($time, 0, 4)\n\t\t);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('unix_to_human'))\n{\n\t/**\n\t * Unix to \"Human\"\n\t *\n\t * Formats Unix timestamp to the following prototype: 2006-08-21 11:35 PM\n\t *\n\t * @param\tint\tUnix timestamp\n\t * @param\tbool\twhether to show seconds\n\t * @param\tstring\tformat: us or euro\n\t * @return\tstring\n\t */\n\tfunction unix_to_human($time = '', $seconds = FALSE, $fmt = 'us')\n\t{\n\t\t$r = date('Y', $time).'-'.date('m', $time).'-'.date('d', $time).' ';\n\n\t\tif ($fmt === 'us')\n\t\t{\n\t\t\t$r .= date('h', $time).':'.date('i', $time);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$r .= date('H', $time).':'.date('i', $time);\n\t\t}\n\n\t\tif ($seconds)\n\t\t{\n\t\t\t$r .= ':'.date('s', $time);\n\t\t}\n\n\t\tif ($fmt === 'us')\n\t\t{\n\t\t\treturn $r.' '.date('A', $time);\n\t\t}\n\n\t\treturn $r;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('human_to_unix'))\n{\n\t/**\n\t * Convert \"human\" date to GMT\n\t *\n\t * Reverses the above process\n\t *\n\t * @param\tstring\tformat: us or euro\n\t * @return\tint\n\t */\n\tfunction human_to_unix($datestr = '')\n\t{\n\t\tif ($datestr === '')\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$datestr = preg_replace('/\\040+/', ' ', trim($datestr));\n\n\t\tif ( ! preg_match('/^(\\d{2}|\\d{4})\\-[0-9]{1,2}\\-[0-9]{1,2}\\s[0-9]{1,2}:[0-9]{1,2}(?::[0-9]{1,2})?(?:\\s[AP]M)?$/i', $datestr))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tsscanf($datestr, '%d-%d-%d %s %s', $year, $month, $day, $time, $ampm);\n\t\tsscanf($time, '%d:%d:%d', $hour, $min, $sec);\n\t\tisset($sec) OR $sec = 0;\n\n\t\tif (isset($ampm))\n\t\t{\n\t\t\t$ampm = strtolower($ampm);\n\n\t\t\tif ($ampm[0] === 'p' && $hour < 12)\n\t\t\t{\n\t\t\t\t$hour += 12;\n\t\t\t}\n\t\t\telseif ($ampm[0] === 'a' && $hour === 12)\n\t\t\t{\n\t\t\t\t$hour = 0;\n\t\t\t}\n\t\t}\n\n\t\treturn mktime($hour, $min, $sec, $month, $day, $year);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('timezone_menu'))\n{\n\t/**\n\t * Timezone Menu\n\t *\n\t * Generates a drop-down menu of timezones.\n\t *\n\t * @param\tstring\ttimezone\n\t * @param\tstring\tclassname\n\t * @param\tstring\tmenu name\n\t * @param\tmixed\tattributes\n\t * @return\tstring\n\t */\n\tfunction timezone_menu($default = 'UTC', $class = '', $name = 'timezones', $attributes = '')\n\t{\n\t\t$CI =& get_instance();\n\t\t$CI->lang->load('date');\n\n\t\t$default = ($default === 'GMT') ? 'UTC' : $default;\n\n\t\t$menu = '<select name=\"'.$name.'\"';\n\n\t\tif ($class !== '')\n\t\t{\n\t\t\t$menu .= ' class=\"'.$class.'\"';\n\t\t}\n\n\t\t$menu .= _stringify_attributes($attributes).\">\\n\";\n\n\t\tforeach (timezones() as $key => $val)\n\t\t{\n\t\t\t$selected = ($default === $key) ? ' selected=\"selected\"' : '';\n\t\t\t$menu .= '<option value=\"'.$key.'\"'.$selected.'>'.$CI->lang->line($key).\"</option>\\n\";\n\t\t}\n\n\t\treturn $menu.'</select>';\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('timezones'))\n{\n\t/**\n\t * Timezones\n\t *\n\t * Returns an array of timezones. This is a helper function\n\t * for various other ones in this library\n\t *\n\t * @param\tstring\ttimezone\n\t * @return\tstring\n\t */\n\tfunction timezones($tz = '')\n\t{\n\t\t// Note: Don't change the order of these even though\n\t\t// some items appear to be in the wrong order\n\n\t\t$zones = array(\n\t\t\t'UM12'\t\t=> -12,\n\t\t\t'UM11'\t\t=> -11,\n\t\t\t'UM10'\t\t=> -10,\n\t\t\t'UM95'\t\t=> -9.5,\n\t\t\t'UM9'\t\t=> -9,\n\t\t\t'UM8'\t\t=> -8,\n\t\t\t'UM7'\t\t=> -7,\n\t\t\t'UM6'\t\t=> -6,\n\t\t\t'UM5'\t\t=> -5,\n\t\t\t'UM45'\t\t=> -4.5,\n\t\t\t'UM4'\t\t=> -4,\n\t\t\t'UM35'\t\t=> -3.5,\n\t\t\t'UM3'\t\t=> -3,\n\t\t\t'UM2'\t\t=> -2,\n\t\t\t'UM1'\t\t=> -1,\n\t\t\t'UTC'\t\t=> 0,\n\t\t\t'UP1'\t\t=> +1,\n\t\t\t'UP2'\t\t=> +2,\n\t\t\t'UP3'\t\t=> +3,\n\t\t\t'UP35'\t\t=> +3.5,\n\t\t\t'UP4'\t\t=> +4,\n\t\t\t'UP45'\t\t=> +4.5,\n\t\t\t'UP5'\t\t=> +5,\n\t\t\t'UP55'\t\t=> +5.5,\n\t\t\t'UP575'\t\t=> +5.75,\n\t\t\t'UP6'\t\t=> +6,\n\t\t\t'UP65'\t\t=> +6.5,\n\t\t\t'UP7'\t\t=> +7,\n\t\t\t'UP8'\t\t=> +8,\n\t\t\t'UP875'\t\t=> +8.75,\n\t\t\t'UP9'\t\t=> +9,\n\t\t\t'UP95'\t\t=> +9.5,\n\t\t\t'UP10'\t\t=> +10,\n\t\t\t'UP105'\t\t=> +10.5,\n\t\t\t'UP11'\t\t=> +11,\n\t\t\t'UP115'\t\t=> +11.5,\n\t\t\t'UP12'\t\t=> +12,\n\t\t\t'UP1275'\t=> +12.75,\n\t\t\t'UP13'\t\t=> +13,\n\t\t\t'UP14'\t\t=> +14\n\t\t);\n\n\t\tif ($tz === '')\n\t\t{\n\t\t\treturn $zones;\n\t\t}\n\n\t\treturn isset($zones[$tz]) ? $zones[$tz] : 0;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('date_range'))\n{\n\t/**\n\t * Date range\n\t *\n\t * Returns a list of dates within a specified period.\n\t *\n\t * @param\tint\tunix_start\tUNIX timestamp of period start date\n\t * @param\tint\tunix_end|days\tUNIX timestamp of period end date\n\t *\t\t\t\t\tor interval in days.\n\t * @param\tmixed\tis_unix\t\tSpecifies whether the second parameter\n\t *\t\t\t\t\tis a UNIX timestamp or a day interval\n\t *\t\t\t\t\t - TRUE or 'unix' for a timestamp\n\t *\t\t\t\t\t - FALSE or 'days' for an interval\n\t * @param\tstring  date_format\tOutput date format, same as in date()\n\t * @return\tarray\n\t */\n\tfunction date_range($unix_start = '', $mixed = '', $is_unix = TRUE, $format = 'Y-m-d')\n\t{\n\t\tif ($unix_start == '' OR $mixed == '' OR $format == '')\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$is_unix = ! ( ! $is_unix OR $is_unix === 'days');\n\n\t\t// Validate input and try strtotime() on invalid timestamps/intervals, just in case\n\t\tif ( ( ! ctype_digit((string) $unix_start) && ($unix_start = @strtotime($unix_start)) === FALSE)\n\t\t\tOR ( ! ctype_digit((string) $mixed) && ($is_unix === FALSE OR ($mixed = @strtotime($mixed)) === FALSE))\n\t\t\tOR ($is_unix === TRUE && $mixed < $unix_start))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif ($is_unix && ($unix_start == $mixed OR date($format, $unix_start) === date($format, $mixed)))\n\t\t{\n\t\t\treturn array(date($format, $unix_start));\n\t\t}\n\n\t\t$range = array();\n\n\t\t$from = new DateTime();\n\t\t$from->setTimestamp($unix_start);\n\n\t\tif ($is_unix)\n\t\t{\n\t\t\t$arg = new DateTime();\n\t\t\t$arg->setTimestamp($mixed);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$arg = (int) $mixed;\n\t\t}\n\n\t\t$period = new DatePeriod($from, new DateInterval('P1D'), $arg);\n\t\tforeach ($period as $date)\n\t\t{\n\t\t\t$range[] = $date->format($format);\n\t\t}\n\n\t\t/* If a period end date was passed to the DatePeriod constructor, it might not\n\t\t * be in our results. Not sure if this is a bug or it's just possible because\n\t\t * the end date might actually be less than 24 hours away from the previously\n\t\t * generated DateTime object, but either way - we have to append it manually.\n\t\t */\n\t\tif ( ! is_int($arg) && $range[count($range) - 1] !== $arg->format($format))\n\t\t{\n\t\t\t$range[] = $arg->format($format);\n\t\t}\n\n\t\treturn $range;\n\t}\n}\n"
  },
  {
    "path": "system/helpers/directory_helper.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Directory Helpers\n *\n * @package\t\tCodeIgniter\n * @subpackage\tHelpers\n * @category\tHelpers\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/helpers/directory_helper.html\n */\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('directory_map'))\n{\n\t/**\n\t * Create a Directory Map\n\t *\n\t * Reads the specified directory and builds an array\n\t * representation of it. Sub-folders contained with the\n\t * directory will be mapped as well.\n\t *\n\t * @param\tstring\t$source_dir\t\tPath to source\n\t * @param\tint\t$directory_depth\tDepth of directories to traverse\n\t *\t\t\t\t\t\t(0 = fully recursive, 1 = current dir, etc)\n\t * @param\tbool\t$hidden\t\t\tWhether to show hidden files\n\t * @return\tarray\n\t */\n\tfunction directory_map($source_dir, $directory_depth = 0, $hidden = FALSE)\n\t{\n\t\tif ($fp = @opendir($source_dir))\n\t\t{\n\t\t\t$filedata\t= array();\n\t\t\t$new_depth\t= $directory_depth - 1;\n\t\t\t$source_dir\t= rtrim($source_dir, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR;\n\n\t\t\twhile (FALSE !== ($file = readdir($fp)))\n\t\t\t{\n\t\t\t\t// Remove '.', '..', and hidden files [optional]\n\t\t\t\tif ($file === '.' OR $file === '..' OR ($hidden === FALSE && $file[0] === '.'))\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tis_dir($source_dir.$file) && $file .= DIRECTORY_SEPARATOR;\n\n\t\t\t\tif (($directory_depth < 1 OR $new_depth > 0) && is_dir($source_dir.$file))\n\t\t\t\t{\n\t\t\t\t\t$filedata[$file] = directory_map($source_dir.$file, $new_depth, $hidden);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$filedata[] = $file;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tclosedir($fp);\n\t\t\treturn $filedata;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n}\n"
  },
  {
    "path": "system/helpers/download_helper.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Download Helpers\n *\n * @package\t\tCodeIgniter\n * @subpackage\tHelpers\n * @category\tHelpers\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/helpers/download_helper.html\n */\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('force_download'))\n{\n\t/**\n\t * Force Download\n\t *\n\t * Generates headers that force a download to happen\n\t *\n\t * @param\tmixed\tfilename (or an array of local file path => destination filename)\n\t * @param\tmixed\tthe data to be downloaded\n\t * @param\tbool\twhether to try and send the actual file MIME type\n\t * @return\tvoid\n\t */\n\tfunction force_download($filename = '', $data = '', $set_mime = FALSE)\n\t{\n\t\tif ($filename === '' OR $data === '')\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\telseif ($data === NULL)\n\t\t{\n\t\t\t// Is $filename an array as ['local source path' => 'destination filename']?\n\t\t\tif (is_array($filename))\n\t\t\t{\n\t\t\t\tif (count($filename) !== 1)\n\t\t\t\t{\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\treset($filename);\n\t\t\t\t$filepath = key($filename);\n\t\t\t\t$filename = current($filename);\n\n\t\t\t\tif (is_int($filepath))\n\t\t\t\t{\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$filepath = $filename;\n\t\t\t\t$filename = explode('/', str_replace(DIRECTORY_SEPARATOR, '/', $filename));\n\t\t\t\t$filename = end($filename);\n\t\t\t}\n\n\t\t\tif ( ! @is_file($filepath) OR ($filesize = @filesize($filepath)) === FALSE)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$filesize = strlen($data);\n\t\t}\n\n\t\t// Set the default MIME type to send\n\t\t$mime = 'application/octet-stream';\n\n\t\t$x = explode('.', $filename);\n\t\t$extension = end($x);\n\n\t\tif ($set_mime === TRUE)\n\t\t{\n\t\t\tif (count($x) === 1 OR $extension === '')\n\t\t\t{\n\t\t\t\t/* If we're going to detect the MIME type,\n\t\t\t\t * we'll need a file extension.\n\t\t\t\t */\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Load the mime types\n\t\t\t$mimes =& get_mimes();\n\n\t\t\t// Only change the default MIME if we can find one\n\t\t\tif (isset($mimes[$extension]))\n\t\t\t{\n\t\t\t\t$mime = is_array($mimes[$extension]) ? $mimes[$extension][0] : $mimes[$extension];\n\t\t\t}\n\t\t}\n\n\t\t/* It was reported that browsers on Android 2.1 (and possibly older as well)\n\t\t * need to have the filename extension upper-cased in order to be able to\n\t\t * download it.\n\t\t *\n\t\t * Reference: http://digiblog.de/2011/04/19/android-and-the-download-file-headers/\n\t\t */\n\t\tif (count($x) !== 1 && isset($_SERVER['HTTP_USER_AGENT']) && preg_match('/Android\\s(1|2\\.[01])/', $_SERVER['HTTP_USER_AGENT']))\n\t\t{\n\t\t\t$x[count($x) - 1] = strtoupper($extension);\n\t\t\t$filename = implode('.', $x);\n\t\t}\n\n\t\t// Clean output buffer\n\t\tif (ob_get_level() !== 0 && @ob_end_clean() === FALSE)\n\t\t{\n\t\t\t@ob_clean();\n\t\t}\n\n\t\t// RFC 6266 allows for multibyte filenames, but only in UTF-8,\n\t\t// so we have to make it conditional ...\n\t\t$charset = strtoupper(config_item('charset'));\n\t\t$utf8_filename = ($charset !== 'UTF-8')\n\t\t\t? get_instance()->utf8->convert_to_utf8($filename, $charset)\n\t\t\t: $filename;\n\t\tisset($utf8_filename[0]) && $utf8_filename = \" filename*=UTF-8''\".rawurlencode($utf8_filename);\n\n\t\t// Generate the server headers\n\t\theader('Content-Type: '.$mime);\n\t\theader('Content-Disposition: attachment; filename=\"'.$filename.'\";'.$utf8_filename);\n\t\theader('Expires: 0');\n\t\theader('Content-Transfer-Encoding: binary');\n\t\theader('Content-Length: '.$filesize);\n\t\theader('Cache-Control: private, no-transform, no-store, must-revalidate');\n\n\t\t// If we have raw data - just dump it\n\t\tif ($data !== NULL)\n\t\t{\n\t\t\texit($data);\n\t\t}\n\n\t\t// Flush the file\n\t\tif (@readfile($filepath) === FALSE)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\texit;\n\t}\n}\n"
  },
  {
    "path": "system/helpers/file_helper.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter File Helpers\n *\n * @package\t\tCodeIgniter\n * @subpackage\tHelpers\n * @category\tHelpers\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/helpers/file_helper.html\n */\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('write_file'))\n{\n\t/**\n\t * Write File\n\t *\n\t * Writes data to the file specified in the path.\n\t * Creates a new file if non-existent.\n\t *\n\t * @param\tstring\t$path\tFile path\n\t * @param\tstring\t$data\tData to write\n\t * @param\tstring\t$mode\tfopen() mode (default: 'wb')\n\t * @return\tbool\n\t */\n\tfunction write_file($path, $data, $mode = 'wb')\n\t{\n\t\tif ( ! $fp = @fopen($path, $mode))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tflock($fp, LOCK_EX);\n\n\t\tfor ($result = $written = 0, $length = strlen($data); $written < $length; $written += $result)\n\t\t{\n\t\t\tif (($result = fwrite($fp, substr($data, $written))) === FALSE)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tflock($fp, LOCK_UN);\n\t\tfclose($fp);\n\n\t\treturn is_int($result);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('delete_files'))\n{\n\t/**\n\t * Delete Files\n\t *\n\t * Deletes all files contained in the supplied directory path.\n\t * Files must be writable or owned by the system in order to be deleted.\n\t * If the second parameter is set to TRUE, any directories contained\n\t * within the supplied base directory will be nuked as well.\n\t *\n\t * @param\tstring\t$path\t\tFile path\n\t * @param\tbool\t$del_dir\tWhether to delete any directories found in the path\n\t * @param\tbool\t$htdocs\t\tWhether to skip deleting .htaccess and index page files\n\t * @param\tint\t$_level\t\tCurrent directory depth level (default: 0; internal use only)\n\t * @return\tbool\n\t */\n\tfunction delete_files($path, $del_dir = FALSE, $htdocs = FALSE, $_level = 0)\n\t{\n\t\t// Trim the trailing slash\n\t\t$path = rtrim($path, '/\\\\');\n\n\t\tif ( ! $current_dir = @opendir($path))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\twhile (FALSE !== ($filename = @readdir($current_dir)))\n\t\t{\n\t\t\tif ($filename !== '.' && $filename !== '..')\n\t\t\t{\n\t\t\t\t$filepath = $path.DIRECTORY_SEPARATOR.$filename;\n\n\t\t\t\tif (is_dir($filepath) && $filename[0] !== '.' && ! is_link($filepath))\n\t\t\t\t{\n\t\t\t\t\tdelete_files($filepath, $del_dir, $htdocs, $_level + 1);\n\t\t\t\t}\n\t\t\t\telseif ($htdocs !== TRUE OR ! preg_match('/^(\\.htaccess|index\\.(html|htm|php)|web\\.config)$/i', $filename))\n\t\t\t\t{\n\t\t\t\t\t@unlink($filepath);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tclosedir($current_dir);\n\n\t\treturn ($del_dir === TRUE && $_level > 0)\n\t\t\t? @rmdir($path)\n\t\t\t: TRUE;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('get_filenames'))\n{\n\t/**\n\t * Get Filenames\n\t *\n\t * Reads the specified directory and builds an array containing the filenames.\n\t * Any sub-folders contained within the specified path are read as well.\n\t *\n\t * @param\tstring\tpath to source\n\t * @param\tbool\twhether to include the path as part of the filename\n\t * @param\tbool\tinternal variable to determine recursion status - do not use in calls\n\t * @return\tarray\n\t */\n\tfunction get_filenames($source_dir, $include_path = FALSE, $_recursion = FALSE)\n\t{\n\t\tstatic $_filedata = array();\n\n\t\tif ($fp = @opendir($source_dir))\n\t\t{\n\t\t\t// reset the array and make sure $source_dir has a trailing slash on the initial call\n\t\t\tif ($_recursion === FALSE)\n\t\t\t{\n\t\t\t\t$_filedata = array();\n\t\t\t\t$source_dir = rtrim(realpath($source_dir), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR;\n\t\t\t}\n\n\t\t\twhile (FALSE !== ($file = readdir($fp)))\n\t\t\t{\n\t\t\t\tif (is_dir($source_dir.$file) && $file[0] !== '.')\n\t\t\t\t{\n\t\t\t\t\tget_filenames($source_dir.$file.DIRECTORY_SEPARATOR, $include_path, TRUE);\n\t\t\t\t}\n\t\t\t\telseif ($file[0] !== '.')\n\t\t\t\t{\n\t\t\t\t\t$_filedata[] = ($include_path === TRUE) ? $source_dir.$file : $file;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tclosedir($fp);\n\t\t\treturn $_filedata;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n}\n\n// --------------------------------------------------------------------\n\nif ( ! function_exists('get_dir_file_info'))\n{\n\t/**\n\t * Get Directory File Information\n\t *\n\t * Reads the specified directory and builds an array containing the filenames,\n\t * filesize, dates, and permissions\n\t *\n\t * Any sub-folders contained within the specified path are read as well.\n\t *\n\t * @param\tstring\tpath to source\n\t * @param\tbool\tLook only at the top level directory specified?\n\t * @param\tbool\tinternal variable to determine recursion status - do not use in calls\n\t * @return\tarray\n\t */\n\tfunction get_dir_file_info($source_dir, $top_level_only = TRUE, $_recursion = FALSE)\n\t{\n\t\tstatic $_filedata = array();\n\t\t$relative_path = $source_dir;\n\n\t\tif ($fp = @opendir($source_dir))\n\t\t{\n\t\t\t// reset the array and make sure $source_dir has a trailing slash on the initial call\n\t\t\tif ($_recursion === FALSE)\n\t\t\t{\n\t\t\t\t$_filedata = array();\n\t\t\t\t$source_dir = rtrim(realpath($source_dir), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR;\n\t\t\t}\n\n\t\t\t// Used to be foreach (scandir($source_dir, 1) as $file), but scandir() is simply not as fast\n\t\t\twhile (FALSE !== ($file = readdir($fp)))\n\t\t\t{\n\t\t\t\tif (is_dir($source_dir.$file) && $file[0] !== '.' && $top_level_only === FALSE)\n\t\t\t\t{\n\t\t\t\t\tget_dir_file_info($source_dir.$file.DIRECTORY_SEPARATOR, $top_level_only, TRUE);\n\t\t\t\t}\n\t\t\t\telseif ($file[0] !== '.')\n\t\t\t\t{\n\t\t\t\t\t$filedata = get_dir_file_info($source_dir.$file);\n\t\t\t\t\t$filedata['relative_path'] = $relative_path;\n\t\t\t\t\t$_filedata[] = $filedata;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tclosedir($fp);\n\t\t\treturn $_filedata;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n}\n\n// --------------------------------------------------------------------\n\nif ( ! function_exists('get_file_info'))\n{\n\t/**\n\t * Get File Info\n\t *\n\t * Given a file and path, returns the name, path, size, date modified\n\t * Second parameter allows you to explicitly declare what information you want returned\n\t * Options are: name, server_path, size, date, readable, writable, executable, fileperms\n\t * Returns FALSE if the file cannot be found.\n\t *\n\t * @param\tstring\tpath to file\n\t * @param\tmixed\tarray or comma separated string of information returned\n\t * @return\tarray\n\t */\n\tfunction get_file_info($file, $returned_values = array('name', 'server_path', 'size', 'date'))\n\t{\n\t\tif ( ! file_exists($file))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif (is_string($returned_values))\n\t\t{\n\t\t\t$returned_values = explode(',', $returned_values);\n\t\t}\n\n\t\tforeach ($returned_values as $key)\n\t\t{\n\t\t\tswitch ($key)\n\t\t\t{\n\t\t\t\tcase 'name':\n\t\t\t\t\t$fileinfo['name'] = basename($file);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'server_path':\n\t\t\t\t\t$fileinfo['server_path'] = $file;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'size':\n\t\t\t\t\t$fileinfo['size'] = filesize($file);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'date':\n\t\t\t\t\t$fileinfo['date'] = filemtime($file);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'readable':\n\t\t\t\t\t$fileinfo['readable'] = is_readable($file);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'writable':\n\t\t\t\t\t$fileinfo['writable'] = is_really_writable($file);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'executable':\n\t\t\t\t\t$fileinfo['executable'] = is_executable($file);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'fileperms':\n\t\t\t\t\t$fileinfo['fileperms'] = fileperms($file);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn $fileinfo;\n\t}\n}\n\n// --------------------------------------------------------------------\n\nif ( ! function_exists('get_mime_by_extension'))\n{\n\t/**\n\t * Get Mime by Extension\n\t *\n\t * Translates a file extension into a mime type based on config/mimes.php.\n\t * Returns FALSE if it can't determine the type, or open the mime config file\n\t *\n\t * Note: this is NOT an accurate way of determining file mime types, and is here strictly as a convenience\n\t * It should NOT be trusted, and should certainly NOT be used for security\n\t *\n\t * @param\tstring\t$filename\tFile name\n\t * @return\tstring\n\t */\n\tfunction get_mime_by_extension($filename)\n\t{\n\t\tstatic $mimes;\n\n\t\tif ( ! is_array($mimes))\n\t\t{\n\t\t\t$mimes = get_mimes();\n\n\t\t\tif (empty($mimes))\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\t\t}\n\n\t\t$extension = strtolower(substr(strrchr($filename, '.'), 1));\n\n\t\tif (isset($mimes[$extension]))\n\t\t{\n\t\t\treturn is_array($mimes[$extension])\n\t\t\t\t? current($mimes[$extension]) // Multiple mime types, just give the first one\n\t\t\t\t: $mimes[$extension];\n\t\t}\n\n\t\treturn FALSE;\n\t}\n}\n\n// --------------------------------------------------------------------\n\nif ( ! function_exists('symbolic_permissions'))\n{\n\t/**\n\t * Symbolic Permissions\n\t *\n\t * Takes a numeric value representing a file's permissions and returns\n\t * standard symbolic notation representing that value\n\t *\n\t * @param\tint\t$perms\tPermissions\n\t * @return\tstring\n\t */\n\tfunction symbolic_permissions($perms)\n\t{\n\t\tif (($perms & 0xC000) === 0xC000)\n\t\t{\n\t\t\t$symbolic = 's'; // Socket\n\t\t}\n\t\telseif (($perms & 0xA000) === 0xA000)\n\t\t{\n\t\t\t$symbolic = 'l'; // Symbolic Link\n\t\t}\n\t\telseif (($perms & 0x8000) === 0x8000)\n\t\t{\n\t\t\t$symbolic = '-'; // Regular\n\t\t}\n\t\telseif (($perms & 0x6000) === 0x6000)\n\t\t{\n\t\t\t$symbolic = 'b'; // Block special\n\t\t}\n\t\telseif (($perms & 0x4000) === 0x4000)\n\t\t{\n\t\t\t$symbolic = 'd'; // Directory\n\t\t}\n\t\telseif (($perms & 0x2000) === 0x2000)\n\t\t{\n\t\t\t$symbolic = 'c'; // Character special\n\t\t}\n\t\telseif (($perms & 0x1000) === 0x1000)\n\t\t{\n\t\t\t$symbolic = 'p'; // FIFO pipe\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$symbolic = 'u'; // Unknown\n\t\t}\n\n\t\t// Owner\n\t\t$symbolic .= (($perms & 0x0100) ? 'r' : '-')\n\t\t\t.(($perms & 0x0080) ? 'w' : '-')\n\t\t\t.(($perms & 0x0040) ? (($perms & 0x0800) ? 's' : 'x' ) : (($perms & 0x0800) ? 'S' : '-'));\n\n\t\t// Group\n\t\t$symbolic .= (($perms & 0x0020) ? 'r' : '-')\n\t\t\t.(($perms & 0x0010) ? 'w' : '-')\n\t\t\t.(($perms & 0x0008) ? (($perms & 0x0400) ? 's' : 'x' ) : (($perms & 0x0400) ? 'S' : '-'));\n\n\t\t// World\n\t\t$symbolic .= (($perms & 0x0004) ? 'r' : '-')\n\t\t\t.(($perms & 0x0002) ? 'w' : '-')\n\t\t\t.(($perms & 0x0001) ? (($perms & 0x0200) ? 't' : 'x' ) : (($perms & 0x0200) ? 'T' : '-'));\n\n\t\treturn $symbolic;\n\t}\n}\n\n// --------------------------------------------------------------------\n\nif ( ! function_exists('octal_permissions'))\n{\n\t/**\n\t * Octal Permissions\n\t *\n\t * Takes a numeric value representing a file's permissions and returns\n\t * a three character string representing the file's octal permissions\n\t *\n\t * @param\tint\t$perms\tPermissions\n\t * @return\tstring\n\t */\n\tfunction octal_permissions($perms)\n\t{\n\t\treturn substr(sprintf('%o', $perms), -3);\n\t}\n}\n"
  },
  {
    "path": "system/helpers/form_helper.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Form Helpers\n *\n * @package\t\tCodeIgniter\n * @subpackage\tHelpers\n * @category\tHelpers\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/helpers/form_helper.html\n */\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('form_open'))\n{\n\t/**\n\t * Form Declaration\n\t *\n\t * Creates the opening portion of the form.\n\t *\n\t * @param\tstring\tthe URI segments of the form destination\n\t * @param\tarray\ta key/value pair of attributes\n\t * @param\tarray\ta key/value pair hidden data\n\t * @return\tstring\n\t */\n\tfunction form_open($action = '', $attributes = array(), $hidden = array())\n\t{\n\t\t$CI =& get_instance();\n\n\t\t// If no action is provided then set to the current url\n\t\tif ( ! $action)\n\t\t{\n\t\t\t$action = $CI->config->site_url($CI->uri->uri_string());\n\t\t}\n\t\t// If an action is not a full URL then turn it into one\n\t\telseif (strpos($action, '://') === FALSE)\n\t\t{\n\t\t\t$action = $CI->config->site_url($action);\n\t\t}\n\n\t\t$attributes = _attributes_to_string($attributes);\n\n\t\tif (stripos($attributes, 'method=') === FALSE)\n\t\t{\n\t\t\t$attributes .= ' method=\"post\"';\n\t\t}\n\n\t\tif (stripos($attributes, 'accept-charset=') === FALSE)\n\t\t{\n\t\t\t$attributes .= ' accept-charset=\"'.strtolower(config_item('charset')).'\"';\n\t\t}\n\n\t\t$form = '<form action=\"'.$action.'\"'.$attributes.\">\\n\";\n\n\t\tif (is_array($hidden))\n\t\t{\n\t\t\tforeach ($hidden as $name => $value)\n\t\t\t{\n\t\t\t\t$form .= '<input type=\"hidden\" name=\"'.$name.'\" value=\"'.html_escape($value).'\" />'.\"\\n\";\n\t\t\t}\n\t\t}\n\n\t\t// Add CSRF field if enabled, but leave it out for GET requests and requests to external websites\n\t\tif ($CI->config->item('csrf_protection') === TRUE && strpos($action, $CI->config->base_url()) !== FALSE && ! stripos($form, 'method=\"get\"'))\n\t\t{\n\t\t\t// Prepend/append random-length \"white noise\" around the CSRF\n\t\t\t// token input, as a form of protection against BREACH attacks\n\t\t\tif (FALSE !== ($noise = $CI->security->get_random_bytes(1)))\n\t\t\t{\n\t\t\t\tlist(, $noise) = unpack('c', $noise);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$noise = mt_rand(-128, 127);\n\t\t\t}\n\n\t\t\t// Prepend if $noise has a negative value, append if positive, do nothing for zero\n\t\t\t$prepend = $append = '';\n\t\t\tif ($noise < 0)\n\t\t\t{\n\t\t\t\t$prepend = str_repeat(\" \", abs($noise));\n\t\t\t}\n\t\t\telseif ($noise > 0)\n\t\t\t{\n\t\t\t\t$append  = str_repeat(\" \", $noise);\n\t\t\t}\n\n\t\t\t$form .= sprintf(\n\t\t\t\t'%s<input type=\"hidden\" name=\"%s\" value=\"%s\" />%s%s',\n\t\t\t\t$prepend,\n\t\t\t\t$CI->security->get_csrf_token_name(),\n\t\t\t\t$CI->security->get_csrf_hash(),\n\t\t\t\t$append,\n\t\t\t\t\"\\n\"\n\t\t\t);\n\t\t}\n\n\t\treturn $form;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('form_open_multipart'))\n{\n\t/**\n\t * Form Declaration - Multipart type\n\t *\n\t * Creates the opening portion of the form, but with \"multipart/form-data\".\n\t *\n\t * @param\tstring\tthe URI segments of the form destination\n\t * @param\tarray\ta key/value pair of attributes\n\t * @param\tarray\ta key/value pair hidden data\n\t * @return\tstring\n\t */\n\tfunction form_open_multipart($action = '', $attributes = array(), $hidden = array())\n\t{\n\t\tif (is_string($attributes))\n\t\t{\n\t\t\t$attributes .= ' enctype=\"multipart/form-data\"';\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$attributes['enctype'] = 'multipart/form-data';\n\t\t}\n\n\t\treturn form_open($action, $attributes, $hidden);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('form_hidden'))\n{\n\t/**\n\t * Hidden Input Field\n\t *\n\t * Generates hidden fields. You can pass a simple key/value string or\n\t * an associative array with multiple values.\n\t *\n\t * @param\tmixed\t$name\t\tField name\n\t * @param\tstring\t$value\t\tField value\n\t * @param\tbool\t$recursing\n\t * @return\tstring\n\t */\n\tfunction form_hidden($name, $value = '', $recursing = FALSE)\n\t{\n\t\tstatic $form;\n\n\t\tif ($recursing === FALSE)\n\t\t{\n\t\t\t$form = \"\\n\";\n\t\t}\n\n\t\tif (is_array($name))\n\t\t{\n\t\t\tforeach ($name as $key => $val)\n\t\t\t{\n\t\t\t\tform_hidden($key, $val, TRUE);\n\t\t\t}\n\n\t\t\treturn $form;\n\t\t}\n\n\t\tif ( ! is_array($value))\n\t\t{\n\t\t\t$form .= '<input type=\"hidden\" name=\"'.$name.'\" value=\"'.html_escape($value).\"\\\" />\\n\";\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach ($value as $k => $v)\n\t\t\t{\n\t\t\t\t$k = is_int($k) ? '' : $k;\n\t\t\t\tform_hidden($name.'['.$k.']', $v, TRUE);\n\t\t\t}\n\t\t}\n\n\t\treturn $form;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('form_input'))\n{\n\t/**\n\t * Text Input Field\n\t *\n\t * @param\tmixed\n\t * @param\tstring\n\t * @param\tmixed\n\t * @return\tstring\n\t */\n\tfunction form_input($data = '', $value = '', $extra = '')\n\t{\n\t\t$defaults = array(\n\t\t\t'type' => 'text',\n\t\t\t'name' => is_array($data) ? '' : $data,\n\t\t\t'value' => $value\n\t\t);\n\n\t\treturn '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra).\" />\\n\";\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('form_password'))\n{\n\t/**\n\t * Password Field\n\t *\n\t * Identical to the input function but adds the \"password\" type\n\t *\n\t * @param\tmixed\n\t * @param\tstring\n\t * @param\tmixed\n\t * @return\tstring\n\t */\n\tfunction form_password($data = '', $value = '', $extra = '')\n\t{\n\t\tis_array($data) OR $data = array('name' => $data);\n\t\t$data['type'] = 'password';\n\t\treturn form_input($data, $value, $extra);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('form_upload'))\n{\n\t/**\n\t * Upload Field\n\t *\n\t * Identical to the input function but adds the \"file\" type\n\t *\n\t * @param\tmixed\n\t * @param\tmixed\n\t * @return\tstring\n\t */\n\tfunction form_upload($data = '', $extra = '')\n\t{\n\t\t$defaults = array('type' => 'file', 'name' => '');\n\t\tis_array($data) OR $data = array('name' => $data);\n\t\t$data['type'] = 'file';\n\n\t\treturn '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra).\" />\\n\";\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('form_textarea'))\n{\n\t/**\n\t * Textarea field\n\t *\n\t * @param\tmixed\t$data\n\t * @param\tstring\t$value\n\t * @param\tmixed\t$extra\n\t * @return\tstring\n\t */\n\tfunction form_textarea($data = '', $value = '', $extra = '')\n\t{\n\t\t$defaults = array(\n\t\t\t'name' => is_array($data) ? '' : $data,\n\t\t\t'cols' => '40',\n\t\t\t'rows' => '10'\n\t\t);\n\n\t\tif ( ! is_array($data) OR ! isset($data['value']))\n\t\t{\n\t\t\t$val = $value;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$val = $data['value'];\n\t\t\tunset($data['value']); // textareas don't use the value attribute\n\t\t}\n\n\t\treturn '<textarea '._parse_form_attributes($data, $defaults)._attributes_to_string($extra).'>'\n\t\t\t.html_escape($val)\n\t\t\t.\"</textarea>\\n\";\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('form_multiselect'))\n{\n\t/**\n\t * Multi-select menu\n\t *\n\t * @param\tstring\n\t * @param\tarray\n\t * @param\tmixed\n\t * @param\tmixed\n\t * @return\tstring\n\t */\n\tfunction form_multiselect($name = '', $options = array(), $selected = array(), $extra = '')\n\t{\n\t\t$extra = _attributes_to_string($extra);\n\t\tif (stripos($extra, 'multiple') === FALSE)\n\t\t{\n\t\t\t$extra .= ' multiple=\"multiple\"';\n\t\t}\n\n\t\treturn form_dropdown($name, $options, $selected, $extra);\n\t}\n}\n\n// --------------------------------------------------------------------\n\nif ( ! function_exists('form_dropdown'))\n{\n\t/**\n\t * Drop-down Menu\n\t *\n\t * @param\tmixed\t$data\n\t * @param\tmixed\t$options\n\t * @param\tmixed\t$selected\n\t * @param\tmixed\t$extra\n\t * @return\tstring\n\t */\n\tfunction form_dropdown($data = '', $options = array(), $selected = array(), $extra = '')\n\t{\n\t\t$defaults = array();\n\n\t\tif (is_array($data))\n\t\t{\n\t\t\tif (isset($data['selected']))\n\t\t\t{\n\t\t\t\t$selected = $data['selected'];\n\t\t\t\tunset($data['selected']); // select tags don't have a selected attribute\n\t\t\t}\n\n\t\t\tif (isset($data['options']))\n\t\t\t{\n\t\t\t\t$options = $data['options'];\n\t\t\t\tunset($data['options']); // select tags don't use an options attribute\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$defaults = array('name' => $data);\n\t\t}\n\n\t\tis_array($selected) OR $selected = array($selected);\n\t\tis_array($options) OR $options = array($options);\n\n\t\t// If no selected state was submitted we will attempt to set it automatically\n\t\tif (empty($selected))\n\t\t{\n\t\t\tif (is_array($data))\n\t\t\t{\n\t\t\t\tif (isset($data['name'], $_POST[$data['name']]))\n\t\t\t\t{\n\t\t\t\t\t$selected = array($_POST[$data['name']]);\n\t\t\t\t}\n\t\t\t}\n\t\t\telseif (isset($_POST[$data]))\n\t\t\t{\n\t\t\t\t$selected = array($_POST[$data]);\n\t\t\t}\n\t\t}\n\n\t\t$extra = _attributes_to_string($extra);\n\n\t\t$multiple = (count($selected) > 1 && stripos($extra, 'multiple') === FALSE) ? ' multiple=\"multiple\"' : '';\n\n\t\t$form = '<select '.rtrim(_parse_form_attributes($data, $defaults)).$extra.$multiple.\">\\n\";\n\n\t\tforeach ($options as $key => $val)\n\t\t{\n\t\t\t$key = (string) $key;\n\n\t\t\tif (is_array($val))\n\t\t\t{\n\t\t\t\tif (empty($val))\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t$form .= '<optgroup label=\"'.$key.\"\\\">\\n\";\n\n\t\t\t\tforeach ($val as $optgroup_key => $optgroup_val)\n\t\t\t\t{\n\t\t\t\t\t$sel = in_array($optgroup_key, $selected) ? ' selected=\"selected\"' : '';\n\t\t\t\t\t$form .= '<option value=\"'.html_escape($optgroup_key).'\"'.$sel.'>'\n\t\t\t\t\t\t.(string) $optgroup_val.\"</option>\\n\";\n\t\t\t\t}\n\n\t\t\t\t$form .= \"</optgroup>\\n\";\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$form .= '<option value=\"'.html_escape($key).'\"'\n\t\t\t\t\t.(in_array($key, $selected) ? ' selected=\"selected\"' : '').'>'\n\t\t\t\t\t.(string) $val.\"</option>\\n\";\n\t\t\t}\n\t\t}\n\n\t\treturn $form.\"</select>\\n\";\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('form_checkbox'))\n{\n\t/**\n\t * Checkbox Field\n\t *\n\t * @param\tmixed\n\t * @param\tstring\n\t * @param\tbool\n\t * @param\tmixed\n\t * @return\tstring\n\t */\n\tfunction form_checkbox($data = '', $value = '', $checked = FALSE, $extra = '')\n\t{\n\t\t$defaults = array('type' => 'checkbox', 'name' => ( ! is_array($data) ? $data : ''), 'value' => $value);\n\n\t\tif (is_array($data) && array_key_exists('checked', $data))\n\t\t{\n\t\t\t$checked = $data['checked'];\n\n\t\t\tif ($checked == FALSE)\n\t\t\t{\n\t\t\t\tunset($data['checked']);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$data['checked'] = 'checked';\n\t\t\t}\n\t\t}\n\n\t\tif ($checked == TRUE)\n\t\t{\n\t\t\t$defaults['checked'] = 'checked';\n\t\t}\n\t\telse\n\t\t{\n\t\t\tunset($defaults['checked']);\n\t\t}\n\n\t\treturn '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra).\" />\\n\";\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('form_radio'))\n{\n\t/**\n\t * Radio Button\n\t *\n\t * @param\tmixed\n\t * @param\tstring\n\t * @param\tbool\n\t * @param\tmixed\n\t * @return\tstring\n\t */\n\tfunction form_radio($data = '', $value = '', $checked = FALSE, $extra = '')\n\t{\n\t\tis_array($data) OR $data = array('name' => $data);\n\t\t$data['type'] = 'radio';\n\n\t\treturn form_checkbox($data, $value, $checked, $extra);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('form_submit'))\n{\n\t/**\n\t * Submit Button\n\t *\n\t * @param\tmixed\n\t * @param\tstring\n\t * @param\tmixed\n\t * @return\tstring\n\t */\n\tfunction form_submit($data = '', $value = '', $extra = '')\n\t{\n\t\t$defaults = array(\n\t\t\t'type' => 'submit',\n\t\t\t'name' => is_array($data) ? '' : $data,\n\t\t\t'value' => $value\n\t\t);\n\n\t\treturn '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra).\" />\\n\";\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('form_reset'))\n{\n\t/**\n\t * Reset Button\n\t *\n\t * @param\tmixed\n\t * @param\tstring\n\t * @param\tmixed\n\t * @return\tstring\n\t */\n\tfunction form_reset($data = '', $value = '', $extra = '')\n\t{\n\t\t$defaults = array(\n\t\t\t'type' => 'reset',\n\t\t\t'name' => is_array($data) ? '' : $data,\n\t\t\t'value' => $value\n\t\t);\n\n\t\treturn '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra).\" />\\n\";\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('form_button'))\n{\n\t/**\n\t * Form Button\n\t *\n\t * @param\tmixed\n\t * @param\tstring\n\t * @param\tmixed\n\t * @return\tstring\n\t */\n\tfunction form_button($data = '', $content = '', $extra = '')\n\t{\n\t\t$defaults = array(\n\t\t\t'name' => is_array($data) ? '' : $data,\n\t\t\t'type' => 'button'\n\t\t);\n\n\t\tif (is_array($data) && isset($data['content']))\n\t\t{\n\t\t\t$content = $data['content'];\n\t\t\tunset($data['content']); // content is not an attribute\n\t\t}\n\n\t\treturn '<button '._parse_form_attributes($data, $defaults)._attributes_to_string($extra).'>'\n\t\t\t.$content\n\t\t\t.\"</button>\\n\";\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('form_label'))\n{\n\t/**\n\t * Form Label Tag\n\t *\n\t * @param\tstring\tThe text to appear onscreen\n\t * @param\tstring\tThe id the label applies to\n\t * @param\tmixed\tAdditional attributes\n\t * @return\tstring\n\t */\n\tfunction form_label($label_text = '', $id = '', $attributes = array())\n\t{\n\n\t\t$label = '<label';\n\n\t\tif ($id !== '')\n\t\t{\n\t\t\t$label .= ' for=\"'.$id.'\"';\n\t\t}\n\n\t\t$label .= _attributes_to_string($attributes);\n\n\t\treturn $label.'>'.$label_text.'</label>';\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('form_fieldset'))\n{\n\t/**\n\t * Fieldset Tag\n\t *\n\t * Used to produce <fieldset><legend>text</legend>.  To close fieldset\n\t * use form_fieldset_close()\n\t *\n\t * @param\tstring\tThe legend text\n\t * @param\tarray\tAdditional attributes\n\t * @return\tstring\n\t */\n\tfunction form_fieldset($legend_text = '', $attributes = array())\n\t{\n\t\t$fieldset = '<fieldset'._attributes_to_string($attributes).\">\\n\";\n\t\tif ($legend_text !== '')\n\t\t{\n\t\t\treturn $fieldset.'<legend>'.$legend_text.\"</legend>\\n\";\n\t\t}\n\n\t\treturn $fieldset;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('form_fieldset_close'))\n{\n\t/**\n\t * Fieldset Close Tag\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tfunction form_fieldset_close($extra = '')\n\t{\n\t\treturn '</fieldset>'.$extra;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('form_close'))\n{\n\t/**\n\t * Form Close Tag\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tfunction form_close($extra = '')\n\t{\n\t\treturn '</form>'.$extra;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('set_value'))\n{\n\t/**\n\t * Form Value\n\t *\n\t * Grabs a value from the POST array for the specified field so you can\n\t * re-populate an input field or textarea. If Form Validation\n\t * is active it retrieves the info from the validation class\n\t *\n\t * @param\tstring\t$field\t\tField name\n\t * @param\tstring\t$default\tDefault value\n\t * @param\tbool\t$html_escape\tWhether to escape HTML special characters or not\n\t * @return\tstring\n\t */\n\tfunction set_value($field, $default = '', $html_escape = TRUE)\n\t{\n\t\t$CI =& get_instance();\n\n\t\t$value = (isset($CI->form_validation) && is_object($CI->form_validation) && $CI->form_validation->has_rule($field))\n\t\t\t? $CI->form_validation->set_value($field, $default)\n\t\t\t: $CI->input->post($field, FALSE);\n\n\t\tisset($value) OR $value = $default;\n\t\treturn ($html_escape) ? html_escape($value) : $value;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('set_select'))\n{\n\t/**\n\t * Set Select\n\t *\n\t * Let's you set the selected value of a <select> menu via data in the POST array.\n\t * If Form Validation is active it retrieves the info from the validation class\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @param\tbool\n\t * @return\tstring\n\t */\n\tfunction set_select($field, $value = '', $default = FALSE)\n\t{\n\t\t$CI =& get_instance();\n\n\t\tif (isset($CI->form_validation) && is_object($CI->form_validation) && $CI->form_validation->has_rule($field))\n\t\t{\n\t\t\treturn $CI->form_validation->set_select($field, $value, $default);\n\t\t}\n\t\telseif (($input = $CI->input->post($field, FALSE)) === NULL)\n\t\t{\n\t\t\treturn ($default === TRUE) ? ' selected=\"selected\"' : '';\n\t\t}\n\n\t\t$value = (string) $value;\n\t\tif (is_array($input))\n\t\t{\n\t\t\t// Note: in_array('', array(0)) returns TRUE, do not use it\n\t\t\tforeach ($input as &$v)\n\t\t\t{\n\t\t\t\tif ($value === $v)\n\t\t\t\t{\n\t\t\t\t\treturn ' selected=\"selected\"';\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn '';\n\t\t}\n\n\t\treturn ($input === $value) ? ' selected=\"selected\"' : '';\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('set_checkbox'))\n{\n\t/**\n\t * Set Checkbox\n\t *\n\t * Let's you set the selected value of a checkbox via the value in the POST array.\n\t * If Form Validation is active it retrieves the info from the validation class\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @param\tbool\n\t * @return\tstring\n\t */\n\tfunction set_checkbox($field, $value = '', $default = FALSE)\n\t{\n\t\t$CI =& get_instance();\n\n\t\tif (isset($CI->form_validation) && is_object($CI->form_validation) && $CI->form_validation->has_rule($field))\n\t\t{\n\t\t\treturn $CI->form_validation->set_checkbox($field, $value, $default);\n\t\t}\n\n\t\t// Form inputs are always strings ...\n\t\t$value = (string) $value;\n\t\t$input = $CI->input->post($field, FALSE);\n\n\t\tif (is_array($input))\n\t\t{\n\t\t\t// Note: in_array('', array(0)) returns TRUE, do not use it\n\t\t\tforeach ($input as &$v)\n\t\t\t{\n\t\t\t\tif ($value === $v)\n\t\t\t\t{\n\t\t\t\t\treturn ' checked=\"checked\"';\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn '';\n\t\t}\n\n\t\t// Unchecked checkbox and radio inputs are not even submitted by browsers ...\n\t\tif ($CI->input->method() === 'post')\n\t\t{\n\t\t\treturn ($input === $value) ? ' checked=\"checked\"' : '';\n\t\t}\n\n\t\treturn ($default === TRUE) ? ' checked=\"checked\"' : '';\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('set_radio'))\n{\n\t/**\n\t * Set Radio\n\t *\n\t * Let's you set the selected value of a radio field via info in the POST array.\n\t * If Form Validation is active it retrieves the info from the validation class\n\t *\n\t * @param\tstring\t$field\n\t * @param\tstring\t$value\n\t * @param\tbool\t$default\n\t * @return\tstring\n\t */\n\tfunction set_radio($field, $value = '', $default = FALSE)\n\t{\n\t\t$CI =& get_instance();\n\n\t\tif (isset($CI->form_validation) && is_object($CI->form_validation) && $CI->form_validation->has_rule($field))\n\t\t{\n\t\t\treturn $CI->form_validation->set_radio($field, $value, $default);\n\t\t}\n\n\t\t// Form inputs are always strings ...\n\t\t$value = (string) $value;\n\t\t$input = $CI->input->post($field, FALSE);\n\n\t\tif (is_array($input))\n\t\t{\n\t\t\t// Note: in_array('', array(0)) returns TRUE, do not use it\n\t\t\tforeach ($input as &$v)\n\t\t\t{\n\t\t\t\tif ($value === $v)\n\t\t\t\t{\n\t\t\t\t\treturn ' checked=\"checked\"';\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn '';\n\t\t}\n\n\t\t// Unchecked checkbox and radio inputs are not even submitted by browsers ...\n\t\tif ($CI->input->method() === 'post')\n\t\t{\n\t\t\treturn ($input === $value) ? ' checked=\"checked\"' : '';\n\t\t}\n\n\t\treturn ($default === TRUE) ? ' checked=\"checked\"' : '';\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('form_error'))\n{\n\t/**\n\t * Form Error\n\t *\n\t * Returns the error for a specific form field. This is a helper for the\n\t * form validation class.\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tfunction form_error($field = '', $prefix = '', $suffix = '')\n\t{\n\t\tif (FALSE === ($OBJ =& _get_validation_object()))\n\t\t{\n\t\t\treturn '';\n\t\t}\n\n\t\treturn $OBJ->error($field, $prefix, $suffix);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('validation_errors'))\n{\n\t/**\n\t * Validation Error String\n\t *\n\t * Returns all the errors associated with a form submission. This is a helper\n\t * function for the form validation class.\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tfunction validation_errors($prefix = '', $suffix = '')\n\t{\n\t\tif (FALSE === ($OBJ =& _get_validation_object()))\n\t\t{\n\t\t\treturn '';\n\t\t}\n\n\t\treturn $OBJ->error_string($prefix, $suffix);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('_parse_form_attributes'))\n{\n\t/**\n\t * Parse the form attributes\n\t *\n\t * Helper function used by some of the form helpers\n\t *\n\t * @param\tarray\t$attributes\tList of attributes\n\t * @param\tarray\t$default\tDefault values\n\t * @return\tstring\n\t */\n\tfunction _parse_form_attributes($attributes, $default)\n\t{\n\t\tif (is_array($attributes))\n\t\t{\n\t\t\tforeach ($default as $key => $val)\n\t\t\t{\n\t\t\t\tif (isset($attributes[$key]))\n\t\t\t\t{\n\t\t\t\t\t$default[$key] = $attributes[$key];\n\t\t\t\t\tunset($attributes[$key]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (count($attributes) > 0)\n\t\t\t{\n\t\t\t\t$default = array_merge($default, $attributes);\n\t\t\t}\n\t\t}\n\n\t\t$att = '';\n\n\t\tforeach ($default as $key => $val)\n\t\t{\n\t\t\tif ($key === 'value')\n\t\t\t{\n\t\t\t\t$val = html_escape($val);\n\t\t\t}\n\t\t\telseif ($key === 'name' && ! strlen($default['name']))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$att .= $key.'=\"'.$val.'\" ';\n\t\t}\n\n\t\treturn $att;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('_attributes_to_string'))\n{\n\t/**\n\t * Attributes To String\n\t *\n\t * Helper function used by some of the form helpers\n\t *\n\t * @param\tmixed\n\t * @return\tstring\n\t */\n\tfunction _attributes_to_string($attributes)\n\t{\n\t\tif (empty($attributes))\n\t\t{\n\t\t\treturn '';\n\t\t}\n\n\t\tif (is_object($attributes))\n\t\t{\n\t\t\t$attributes = (array) $attributes;\n\t\t}\n\n\t\tif (is_array($attributes))\n\t\t{\n\t\t\t$atts = '';\n\n\t\t\tforeach ($attributes as $key => $val)\n\t\t\t{\n\t\t\t\t$atts .= ' '.$key.'=\"'.$val.'\"';\n\t\t\t}\n\n\t\t\treturn $atts;\n\t\t}\n\n\t\tif (is_string($attributes))\n\t\t{\n\t\t\treturn ' '.$attributes;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('_get_validation_object'))\n{\n\t/**\n\t * Validation Object\n\t *\n\t * Determines what the form validation class was instantiated as, fetches\n\t * the object and returns it.\n\t *\n\t * @return\tmixed\n\t */\n\tfunction &_get_validation_object()\n\t{\n\t\t$CI =& get_instance();\n\n\t\t// We set this as a variable since we're returning by reference.\n\t\t$return = FALSE;\n\n\t\tif (FALSE !== ($object = $CI->load->is_loaded('Form_validation')))\n\t\t{\n\t\t\tif ( ! isset($CI->$object) OR ! is_object($CI->$object))\n\t\t\t{\n\t\t\t\treturn $return;\n\t\t\t}\n\n\t\t\treturn $CI->$object;\n\t\t}\n\n\t\treturn $return;\n\t}\n}\n"
  },
  {
    "path": "system/helpers/html_helper.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter HTML Helpers\n *\n * @package\t\tCodeIgniter\n * @subpackage\tHelpers\n * @category\tHelpers\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/helpers/html_helper.html\n */\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('heading'))\n{\n\t/**\n\t * Heading\n\t *\n\t * Generates an HTML heading tag.\n\t *\n\t * @param\tstring\tcontent\n\t * @param\tint\theading level\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tfunction heading($data = '', $h = '1', $attributes = '')\n\t{\n\t\treturn '<h'.$h._stringify_attributes($attributes).'>'.$data.'</h'.$h.'>';\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('ul'))\n{\n\t/**\n\t * Unordered List\n\t *\n\t * Generates an HTML unordered list from an single or multi-dimensional array.\n\t *\n\t * @param\tarray\n\t * @param\tmixed\n\t * @return\tstring\n\t */\n\tfunction ul($list, $attributes = '')\n\t{\n\t\treturn _list('ul', $list, $attributes);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('ol'))\n{\n\t/**\n\t * Ordered List\n\t *\n\t * Generates an HTML ordered list from an single or multi-dimensional array.\n\t *\n\t * @param\tarray\n\t * @param\tmixed\n\t * @return\tstring\n\t */\n\tfunction ol($list, $attributes = '')\n\t{\n\t\treturn _list('ol', $list, $attributes);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('_list'))\n{\n\t/**\n\t * Generates the list\n\t *\n\t * Generates an HTML ordered list from an single or multi-dimensional array.\n\t *\n\t * @param\tstring\n\t * @param\tmixed\n\t * @param\tmixed\n\t * @param\tint\n\t * @return\tstring\n\t */\n\tfunction _list($type = 'ul', $list = array(), $attributes = '', $depth = 0)\n\t{\n\t\t// If an array wasn't submitted there's nothing to do...\n\t\tif ( ! is_array($list))\n\t\t{\n\t\t\treturn $list;\n\t\t}\n\n\t\t// Set the indentation based on the depth\n\t\t$out = str_repeat(' ', $depth)\n\t\t\t// Write the opening list tag\n\t\t\t.'<'.$type._stringify_attributes($attributes).\">\\n\";\n\n\t\t// Cycle through the list elements.  If an array is\n\t\t// encountered we will recursively call _list()\n\n\t\tstatic $_last_list_item = '';\n\t\tforeach ($list as $key => $val)\n\t\t{\n\t\t\t$_last_list_item = $key;\n\n\t\t\t$out .= str_repeat(' ', $depth + 2).'<li>';\n\n\t\t\tif ( ! is_array($val))\n\t\t\t{\n\t\t\t\t$out .= $val;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$out .= $_last_list_item.\"\\n\"._list($type, $val, '', $depth + 4).str_repeat(' ', $depth + 2);\n\t\t\t}\n\n\t\t\t$out .= \"</li>\\n\";\n\t\t}\n\n\t\t// Set the indentation for the closing tag and apply it\n\t\treturn $out.str_repeat(' ', $depth).'</'.$type.\">\\n\";\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('img'))\n{\n\t/**\n\t * Image\n\t *\n\t * Generates an <img /> element\n\t *\n\t * @param\tmixed\n\t * @param\tbool\n\t * @param\tmixed\n\t * @return\tstring\n\t */\n\tfunction img($src = '', $index_page = FALSE, $attributes = '')\n\t{\n\t\tif ( ! is_array($src) )\n\t\t{\n\t\t\t$src = array('src' => $src);\n\t\t}\n\n\t\t// If there is no alt attribute defined, set it to an empty string\n\t\tif ( ! isset($src['alt']))\n\t\t{\n\t\t\t$src['alt'] = '';\n\t\t}\n\n\t\t$img = '<img';\n\n\t\tforeach ($src as $k => $v)\n\t\t{\n\t\t\tif ($k === 'src' && ! preg_match('#^(data:[a-z,;])|(([a-z]+:)?(?<!data:)//)#i', $v))\n\t\t\t{\n\t\t\t\tif ($index_page === TRUE)\n\t\t\t\t{\n\t\t\t\t\t$img .= ' src=\"'.get_instance()->config->site_url($v).'\"';\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$img .= ' src=\"'.get_instance()->config->base_url($v).'\"';\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$img .= ' '.$k.'=\"'.$v.'\"';\n\t\t\t}\n\t\t}\n\n\t\treturn $img._stringify_attributes($attributes).' />';\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('doctype'))\n{\n\t/**\n\t * Doctype\n\t *\n\t * Generates a page document type declaration\n\t *\n\t * Examples of valid options: html5, xhtml-11, xhtml-strict, xhtml-trans,\n\t * xhtml-frame, html4-strict, html4-trans, and html4-frame.\n\t * All values are saved in the doctypes config file.\n\t *\n\t * @param\tstring\ttype\tThe doctype to be generated\n\t * @return\tstring\n\t */\n\tfunction doctype($type = 'html5')\n\t{\n\t\tstatic $doctypes;\n\n\t\tif ( ! is_array($doctypes))\n\t\t{\n\t\t\tif (file_exists(APPPATH.'config/doctypes.php'))\n\t\t\t{\n\t\t\t\tinclude(APPPATH.'config/doctypes.php');\n\t\t\t}\n\n\t\t\tif (file_exists(APPPATH.'config/'.ENVIRONMENT.'/doctypes.php'))\n\t\t\t{\n\t\t\t\tinclude(APPPATH.'config/'.ENVIRONMENT.'/doctypes.php');\n\t\t\t}\n\n\t\t\tif (empty($_doctypes) OR ! is_array($_doctypes))\n\t\t\t{\n\t\t\t\t$doctypes = array();\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\t$doctypes = $_doctypes;\n\t\t}\n\n\t\treturn isset($doctypes[$type]) ? $doctypes[$type] : FALSE;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('link_tag'))\n{\n\t/**\n\t * Link\n\t *\n\t * Generates link to a CSS file\n\t *\n\t * @param\tmixed\tstylesheet hrefs or an array\n\t * @param\tstring\trel\n\t * @param\tstring\ttype\n\t * @param\tstring\ttitle\n\t * @param\tstring\tmedia\n\t * @param\tbool\tshould index_page be added to the css path\n\t * @return\tstring\n\t */\n\tfunction link_tag($href = '', $rel = 'stylesheet', $type = 'text/css', $title = '', $media = '', $index_page = FALSE)\n\t{\n\t\t$CI =& get_instance();\n\t\t$link = '<link ';\n\n\t\tif (is_array($href))\n\t\t{\n\t\t\tforeach ($href as $k => $v)\n\t\t\t{\n\t\t\t\tif ($k === 'href' && ! preg_match('#^([a-z]+:)?//#i', $v))\n\t\t\t\t{\n\t\t\t\t\tif ($index_page === TRUE)\n\t\t\t\t\t{\n\t\t\t\t\t\t$link .= 'href=\"'.$CI->config->site_url($v).'\" ';\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t$link .= 'href=\"'.$CI->config->base_url($v).'\" ';\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$link .= $k.'=\"'.$v.'\" ';\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (preg_match('#^([a-z]+:)?//#i', $href))\n\t\t\t{\n\t\t\t\t$link .= 'href=\"'.$href.'\" ';\n\t\t\t}\n\t\t\telseif ($index_page === TRUE)\n\t\t\t{\n\t\t\t\t$link .= 'href=\"'.$CI->config->site_url($href).'\" ';\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$link .= 'href=\"'.$CI->config->base_url($href).'\" ';\n\t\t\t}\n\n\t\t\t$link .= 'rel=\"'.$rel.'\" type=\"'.$type.'\" ';\n\n\t\t\tif ($media !== '')\n\t\t\t{\n\t\t\t\t$link .= 'media=\"'.$media.'\" ';\n\t\t\t}\n\n\t\t\tif ($title !== '')\n\t\t\t{\n\t\t\t\t$link .= 'title=\"'.$title.'\" ';\n\t\t\t}\n\t\t}\n\n\t\treturn $link.\"/>\\n\";\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('meta'))\n{\n\t/**\n\t * Generates meta tags from an array of key/values\n\t *\n\t * @param\tarray\n\t * @param\tstring\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tfunction meta($name = '', $content = '', $type = 'name', $newline = \"\\n\")\n\t{\n\t\t// Since we allow the data to be passes as a string, a simple array\n\t\t// or a multidimensional one, we need to do a little prepping.\n\t\tif ( ! is_array($name))\n\t\t{\n\t\t\t$name = array(array('name' => $name, 'content' => $content, 'type' => $type, 'newline' => $newline));\n\t\t}\n\t\telseif (isset($name['name']))\n\t\t{\n\t\t\t// Turn single array into multidimensional\n\t\t\t$name = array($name);\n\t\t}\n\n\t\t$allowed_types = array('charset', 'http-equiv', 'name', 'property');\n\t\t$str = '';\n\t\tforeach ($name as $meta)\n\t\t{\n\t\t\t// This is to preserve BC with pre-3.1 versions where only\n\t\t\t// 'http-equiv' (default) and 'name' were supported.\n\t\t\tif (isset($meta['type']))\n\t\t\t{\n\t\t\t\tif ($meta['type'] === 'equiv')\n\t\t\t\t{\n\t\t\t\t\t$meta['type'] = 'http-equiv';\n\t\t\t\t}\n\t\t\t\telseif ( ! in_array($meta['type'], $allowed_types, TRUE))\n\t\t\t\t{\n\t\t\t\t\t$meta['type'] = 'name';\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$type    = isset($meta['type'])    ? $meta['type']    : 'name';\n\t\t\t$name    = isset($meta['name'])    ? $meta['name']    : '';\n\t\t\t$content = isset($meta['content']) ? $meta['content'] : '';\n\t\t\t$newline = isset($meta['newline']) ? $meta['newline'] : \"\\n\";\n\n\t\t\t$str .= '<meta '.$type.'=\"'.$name.($type === 'charset' ? '' : '\" content=\"'.$content).'\" />'.$newline;\n\t\t}\n\n\t\treturn $str;\n\t}\n}\n"
  },
  {
    "path": "system/helpers/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/helpers/inflector_helper.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Inflector Helpers\n *\n * @package\t\tCodeIgniter\n * @subpackage\tHelpers\n * @category\tHelpers\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/helpers/inflector_helper.html\n */\n\n// --------------------------------------------------------------------\n\nif ( ! function_exists('singular'))\n{\n\t/**\n\t * Singular\n\t *\n\t * Takes a plural word and makes it singular\n\t *\n\t * @param\tstring\t$str\tInput string\n\t * @return\tstring\n\t */\n\tfunction singular($str)\n\t{\n\t\t$result = strval($str);\n\n\t\tif ( ! word_is_countable($result))\n\t\t{\n\t\t\treturn $result;\n\t\t}\n\n\t\t$singular_rules = array(\n\t\t\t'/(matr)ices$/'\t\t=> '\\1ix',\n\t\t\t'/(vert|ind)ices$/'\t=> '\\1ex',\n\t\t\t'/^(ox)en/'\t\t=> '\\1',\n\t\t\t'/(alias)es$/'\t\t=> '\\1',\n\t\t\t'/([octop|vir])i$/'\t=> '\\1us',\n\t\t\t'/(cris|ax|test)es$/'\t=> '\\1is',\n\t\t\t'/(shoe)s$/'\t\t=> '\\1',\n\t\t\t'/(o)es$/'\t\t=> '\\1',\n\t\t\t'/(bus|campus)es$/'\t=> '\\1',\n\t\t\t'/([m|l])ice$/'\t\t=> '\\1ouse',\n\t\t\t'/(x|ch|ss|sh)es$/'\t=> '\\1',\n\t\t\t'/(m)ovies$/'\t\t=> '\\1\\2ovie',\n\t\t\t'/(s)eries$/'\t\t=> '\\1\\2eries',\n\t\t\t'/([^aeiouy]|qu)ies$/'\t=> '\\1y',\n\t\t\t'/([lr])ves$/'\t\t=> '\\1f',\n\t\t\t'/(tive)s$/'\t\t=> '\\1',\n\t\t\t'/(hive)s$/'\t\t=> '\\1',\n\t\t\t'/([^f])ves$/'\t\t=> '\\1fe',\n\t\t\t'/(^analy)ses$/'\t=> '\\1sis',\n\t\t\t'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/' => '\\1\\2sis',\n\t\t\t'/([ti])a$/'\t\t=> '\\1um',\n\t\t\t'/(p)eople$/'\t\t=> '\\1\\2erson',\n\t\t\t'/(m)en$/'\t\t=> '\\1an',\n\t\t\t'/(s)tatuses$/'\t\t=> '\\1\\2tatus',\n\t\t\t'/(c)hildren$/'\t\t=> '\\1\\2hild',\n\t\t\t'/(n)ews$/'\t\t=> '\\1\\2ews',\n\t\t\t'/(quiz)zes$/'\t\t=> '\\1',\n\t\t\t'/([^us])s$/'\t\t=> '\\1'\n\t\t);\n\n\t\tforeach ($singular_rules as $rule => $replacement)\n\t\t{\n\t\t\tif (preg_match($rule, $result))\n\t\t\t{\n\t\t\t\t$result = preg_replace($rule, $replacement, $result);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn $result;\n\t}\n}\n\n// --------------------------------------------------------------------\n\nif ( ! function_exists('plural'))\n{\n\t/**\n\t * Plural\n\t *\n\t * Takes a singular word and makes it plural\n\t *\n\t * @param\tstring\t$str\tInput string\n\t * @return\tstring\n\t */\n\tfunction plural($str)\n\t{\n\t\t$result = strval($str);\n\n\t\tif ( ! word_is_countable($result))\n\t\t{\n\t\t\treturn $result;\n\t\t}\n\n\t\t$plural_rules = array(\n\t\t\t'/(quiz)$/'                => '\\1zes',      // quizzes\n\t\t\t'/^(ox)$/'                 => '\\1\\2en',     // ox\n\t\t\t'/([m|l])ouse$/'           => '\\1ice',      // mouse, louse\n\t\t\t'/(matr|vert|ind)ix|ex$/'  => '\\1ices',     // matrix, vertex, index\n\t\t\t'/(x|ch|ss|sh)$/'          => '\\1es',       // search, switch, fix, box, process, address\n\t\t\t'/([^aeiouy]|qu)y$/'       => '\\1ies',      // query, ability, agency\n\t\t\t'/(hive)$/'                => '\\1s',        // archive, hive\n\t\t\t'/(?:([^f])fe|([lr])f)$/'  => '\\1\\2ves',    // half, safe, wife\n\t\t\t'/sis$/'                   => 'ses',        // basis, diagnosis\n\t\t\t'/([ti])um$/'              => '\\1a',        // datum, medium\n\t\t\t'/(p)erson$/'              => '\\1eople',    // person, salesperson\n\t\t\t'/(m)an$/'                 => '\\1en',       // man, woman, spokesman\n\t\t\t'/(c)hild$/'               => '\\1hildren',  // child\n\t\t\t'/(buffal|tomat)o$/'       => '\\1\\2oes',    // buffalo, tomato\n\t\t\t'/(bu|campu)s$/'           => '\\1\\2ses',    // bus, campus\n\t\t\t'/(alias|status|virus)$/'  => '\\1es',       // alias\n\t\t\t'/(octop)us$/'             => '\\1i',        // octopus\n\t\t\t'/(ax|cris|test)is$/'      => '\\1es',       // axis, crisis\n\t\t\t'/s$/'                     => 's',          // no change (compatibility)\n\t\t\t'/$/'                      => 's',\n\t\t);\n\n\t\tforeach ($plural_rules as $rule => $replacement)\n\t\t{\n\t\t\tif (preg_match($rule, $result))\n\t\t\t{\n\t\t\t\t$result = preg_replace($rule, $replacement, $result);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn $result;\n\t}\n}\n\n// --------------------------------------------------------------------\n\nif ( ! function_exists('camelize'))\n{\n\t/**\n\t * Camelize\n\t *\n\t * Takes multiple words separated by spaces or underscores and camelizes them\n\t *\n\t * @param\tstring\t$str\tInput string\n\t * @return\tstring\n\t */\n\tfunction camelize($str)\n\t{\n\t\treturn strtolower($str[0]).substr(str_replace(' ', '', ucwords(preg_replace('/[\\s_]+/', ' ', $str))), 1);\n\t}\n}\n\n// --------------------------------------------------------------------\n\nif ( ! function_exists('underscore'))\n{\n\t/**\n\t * Underscore\n\t *\n\t * Takes multiple words separated by spaces and underscores them\n\t *\n\t * @param\tstring\t$str\tInput string\n\t * @return\tstring\n\t */\n\tfunction underscore($str)\n\t{\n\t\treturn preg_replace('/[\\s]+/', '_', trim(MB_ENABLED ? mb_strtolower($str) : strtolower($str)));\n\t}\n}\n\n// --------------------------------------------------------------------\n\nif ( ! function_exists('humanize'))\n{\n\t/**\n\t * Humanize\n\t *\n\t * Takes multiple words separated by the separator and changes them to spaces\n\t *\n\t * @param\tstring\t$str\t\tInput string\n\t * @param \tstring\t$separator\tInput separator\n\t * @return\tstring\n\t */\n\tfunction humanize($str, $separator = '_')\n\t{\n\t\treturn ucwords(preg_replace('/['.preg_quote($separator).']+/', ' ', trim(MB_ENABLED ? mb_strtolower($str) : strtolower($str))));\n\t}\n}\n\n// --------------------------------------------------------------------\n\nif ( ! function_exists('word_is_countable'))\n{\n\t/**\n\t * Checks if the given word has a plural version.\n\t *\n\t * @param\tstring\t$word\tWord to check\n\t * @return\tbool\n\t */\n\tfunction word_is_countable($word)\n\t{\n\t\treturn ! in_array(\n\t\t\tstrtolower($word),\n\t\t\tarray(\n\t\t\t\t'audio',\n\t\t\t\t'bison',\n\t\t\t\t'chassis',\n\t\t\t\t'compensation',\n\t\t\t\t'coreopsis',\n\t\t\t\t'data',\n\t\t\t\t'deer',\n\t\t\t\t'education',\n\t\t\t\t'emoji',\n\t\t\t\t'equipment',\n\t\t\t\t'fish',\n\t\t\t\t'furniture',\n\t\t\t\t'gold',\n\t\t\t\t'information',\n\t\t\t\t'knowledge',\n\t\t\t\t'love',\n\t\t\t\t'rain',\n\t\t\t\t'money',\n\t\t\t\t'moose',\n\t\t\t\t'nutrition',\n\t\t\t\t'offspring',\n\t\t\t\t'plankton',\n\t\t\t\t'pokemon',\n\t\t\t\t'police',\n\t\t\t\t'rice',\n\t\t\t\t'series',\n\t\t\t\t'sheep',\n\t\t\t\t'species',\n\t\t\t\t'swine',\n\t\t\t\t'traffic',\n\t\t\t\t'wheat'\n\t\t\t)\n\t\t);\n\t}\n}\n\n// --------------------------------------------------------------------\n\nif ( ! function_exists('is_countable'))\n{\n\tfunction is_countable($word)\n\t{\n\t\ttrigger_error('is_countable() is a native PHP function since version 7.3.0; use word_is_countable() instead', E_USER_WARNING);\n\t\treturn word_is_countable($word);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('ordinal_format'))\n{\n\t/**\n\t * Returns the English ordinal numeral for a given number\n\t *\n\t * @param  int    $number\n\t * @return string\n\t */\n\tfunction ordinal_format($number)\n\t{\n\t\tif ( ! ctype_digit((string) $number) OR $number < 1)\n\t\t{\n\t\t\treturn $number;\n\t\t}\n\n\t\t$last_digit = array(\n\t\t\t0 => 'th',\n\t\t\t1 => 'st',\n\t\t\t2 => 'nd',\n\t\t\t3 => 'rd',\n\t\t\t4 => 'th',\n\t\t\t5 => 'th',\n\t\t\t6 => 'th',\n\t\t\t7 => 'th',\n\t\t\t8 => 'th',\n\t\t\t9 => 'th'\n\t\t);\n\n\t\tif (($number % 100) >= 11 && ($number % 100) <= 13)\n\t\t{\n\t\t\treturn $number.'th';\n\t\t}\n\n\t\treturn $number.$last_digit[$number % 10];\n\t}\n}\n"
  },
  {
    "path": "system/helpers/language_helper.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Language Helpers\n *\n * @package\t\tCodeIgniter\n * @subpackage\tHelpers\n * @category\tHelpers\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/helpers/language_helper.html\n */\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('lang'))\n{\n\t/**\n\t * Lang\n\t *\n\t * Fetches a language variable and optionally outputs a form label\n\t *\n\t * @param\tstring\t$line\t\tThe language line\n\t * @param\tstring\t$for\t\tThe \"for\" value (id of the form element)\n\t * @param\tarray\t$attributes\tAny additional HTML attributes\n\t * @return\tstring\n\t */\n\tfunction lang($line, $for = '', $attributes = array())\n\t{\n\t\t$line = get_instance()->lang->line($line);\n\n\t\tif ($for !== '')\n\t\t{\n\t\t\t$line = '<label for=\"'.$for.'\"'._stringify_attributes($attributes).'>'.$line.'</label>';\n\t\t}\n\n\t\treturn $line;\n\t}\n}\n"
  },
  {
    "path": "system/helpers/number_helper.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Number Helpers\n *\n * @package\t\tCodeIgniter\n * @subpackage\tHelpers\n * @category\tHelpers\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/helpers/number_helper.html\n */\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('byte_format'))\n{\n\t/**\n\t * Formats a numbers as bytes, based on size, and adds the appropriate suffix\n\t *\n\t * @param\tmixed\twill be cast as int\n\t * @param\tint\n\t * @return\tstring\n\t */\n\tfunction byte_format($num, $precision = 1)\n\t{\n\t\t$CI =& get_instance();\n\t\t$CI->lang->load('number');\n\n\t\tif ($num >= 1000000000000)\n\t\t{\n\t\t\t$num = round($num / 1099511627776, $precision);\n\t\t\t$unit = $CI->lang->line('terabyte_abbr');\n\t\t}\n\t\telseif ($num >= 1000000000)\n\t\t{\n\t\t\t$num = round($num / 1073741824, $precision);\n\t\t\t$unit = $CI->lang->line('gigabyte_abbr');\n\t\t}\n\t\telseif ($num >= 1000000)\n\t\t{\n\t\t\t$num = round($num / 1048576, $precision);\n\t\t\t$unit = $CI->lang->line('megabyte_abbr');\n\t\t}\n\t\telseif ($num >= 1000)\n\t\t{\n\t\t\t$num = round($num / 1024, $precision);\n\t\t\t$unit = $CI->lang->line('kilobyte_abbr');\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$unit = $CI->lang->line('bytes');\n\t\t\treturn number_format($num).' '.$unit;\n\t\t}\n\n\t\treturn number_format($num, $precision).' '.$unit;\n\t}\n}\n"
  },
  {
    "path": "system/helpers/path_helper.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Path Helpers\n *\n * @package\t\tCodeIgniter\n * @subpackage\tHelpers\n * @category\tHelpers\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/helpers/path_helper.html\n */\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('set_realpath'))\n{\n\t/**\n\t * Set Realpath\n\t *\n\t * @param\tstring\n\t * @param\tbool\tchecks to see if the path exists\n\t * @return\tstring\n\t */\n\tfunction set_realpath($path, $check_existance = FALSE)\n\t{\n\t\t// Security check to make sure the path is NOT a URL. No remote file inclusion!\n\t\tif (preg_match('#^(http:\\/\\/|https:\\/\\/|www\\.|ftp|php:\\/\\/)#i', $path) OR filter_var($path, FILTER_VALIDATE_IP) === $path)\n\t\t{\n\t\t\tshow_error('The path you submitted must be a local server path, not a URL');\n\t\t}\n\n\t\t// Resolve the path\n\t\tif (realpath($path) !== FALSE)\n\t\t{\n\t\t\t$path = realpath($path);\n\t\t}\n\t\telseif ($check_existance && ! is_dir($path) && ! is_file($path))\n\t\t{\n\t\t\tshow_error('Not a valid path: '.$path);\n\t\t}\n\n\t\t// Add a trailing slash, if this is a directory\n\t\treturn is_dir($path) ? rtrim($path, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR : $path;\n\t}\n}\n"
  },
  {
    "path": "system/helpers/security_helper.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Security Helpers\n *\n * @package\t\tCodeIgniter\n * @subpackage\tHelpers\n * @category\tHelpers\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/helpers/security_helper.html\n */\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('xss_clean'))\n{\n\t/**\n\t * XSS Filtering\n\t *\n\t * @param\tstring\n\t * @param\tbool\twhether or not the content is an image file\n\t * @return\tstring\n\t */\n\tfunction xss_clean($str, $is_image = FALSE)\n\t{\n\t\treturn get_instance()->security->xss_clean($str, $is_image);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('sanitize_filename'))\n{\n\t/**\n\t * Sanitize Filename\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tfunction sanitize_filename($filename)\n\t{\n\t\treturn get_instance()->security->sanitize_filename($filename);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('strip_image_tags'))\n{\n\t/**\n\t * Strip Image Tags\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tfunction strip_image_tags($str)\n\t{\n\t\treturn get_instance()->security->strip_image_tags($str);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('encode_php_tags'))\n{\n\t/**\n\t * Convert PHP tags to entities\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tfunction encode_php_tags($str)\n\t{\n\t\treturn str_replace(array('<?', '?>'), array('&lt;?', '?&gt;'), $str);\n\t}\n}\n"
  },
  {
    "path": "system/helpers/string_helper.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter String Helpers\n *\n * @package\t\tCodeIgniter\n * @subpackage\tHelpers\n * @category\tHelpers\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/helpers/string_helper.html\n */\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('strip_slashes'))\n{\n\t/**\n\t * Strip Slashes\n\t *\n\t * Removes slashes contained in a string or in an array\n\t *\n\t * @param\tmixed\tstring or array\n\t * @return\tmixed\tstring or array\n\t */\n\tfunction strip_slashes($str)\n\t{\n\t\tif ( ! is_array($str))\n\t\t{\n\t\t\treturn stripslashes($str);\n\t\t}\n\n\t\tforeach ($str as $key => $val)\n\t\t{\n\t\t\t$str[$key] = strip_slashes($val);\n\t\t}\n\n\t\treturn $str;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('strip_quotes'))\n{\n\t/**\n\t * Strip Quotes\n\t *\n\t * Removes single and double quotes from a string\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tfunction strip_quotes($str)\n\t{\n\t\treturn str_replace(array('\"', \"'\"), '', $str);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('quotes_to_entities'))\n{\n\t/**\n\t * Quotes to Entities\n\t *\n\t * Converts single and double quotes to entities\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tfunction quotes_to_entities($str)\n\t{\n\t\treturn str_replace(array(\"\\'\",\"\\\"\",\"'\",'\"'), array(\"&#39;\",\"&quot;\",\"&#39;\",\"&quot;\"), $str);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('reduce_double_slashes'))\n{\n\t/**\n\t * Reduce Double Slashes\n\t *\n\t * Converts double slashes in a string to a single slash,\n\t * except those found in http://\n\t *\n\t * http://www.some-site.com//index.php\n\t *\n\t * becomes:\n\t *\n\t * http://www.some-site.com/index.php\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tfunction reduce_double_slashes($str)\n\t{\n\t\treturn preg_replace('#(^|[^:])//+#', '\\\\1/', $str);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('reduce_multiples'))\n{\n\t/**\n\t * Reduce Multiples\n\t *\n\t * Reduces multiple instances of a particular character.  Example:\n\t *\n\t * Fred, Bill,, Joe, Jimmy\n\t *\n\t * becomes:\n\t *\n\t * Fred, Bill, Joe, Jimmy\n\t *\n\t * @param\tstring\n\t * @param\tstring\tthe character you wish to reduce\n\t * @param\tbool\tTRUE/FALSE - whether to trim the character from the beginning/end\n\t * @return\tstring\n\t */\n\tfunction reduce_multiples($str, $character = ',', $trim = FALSE)\n\t{\n\t\t$str = preg_replace('#'.preg_quote($character, '#').'{2,}#', $character, $str);\n\t\treturn ($trim === TRUE) ? trim($str, $character) : $str;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('random_string'))\n{\n\t/**\n\t * Create a \"Random\" String\n\t *\n\t * @param\tstring\ttype of random string.  basic, alpha, alnum, numeric, nozero, md5 and sha1\n\t * @param\tint\tnumber of characters\n\t * @return\tstring\n\t */\n\tfunction random_string($type = 'alnum', $len = 8)\n\t{\n\t\tswitch ($type)\n\t\t{\n\t\t\tcase 'basic':\n\t\t\t\treturn mt_rand();\n\t\t\tcase 'alnum':\n\t\t\tcase 'numeric':\n\t\t\tcase 'nozero':\n\t\t\tcase 'alpha':\n\t\t\t\tswitch ($type)\n\t\t\t\t{\n\t\t\t\t\tcase 'alpha':\n\t\t\t\t\t\t$pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'alnum':\n\t\t\t\t\t\t$pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'numeric':\n\t\t\t\t\t\t$pool = '0123456789';\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'nozero':\n\t\t\t\t\t\t$pool = '123456789';\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\treturn substr(str_shuffle(str_repeat($pool, ceil($len / strlen($pool)))), 0, $len);\n\t\t\tcase 'md5':\n\t\t\t\treturn md5(uniqid(mt_rand()));\n\t\t\tcase 'sha1':\n\t\t\t\treturn sha1(uniqid(mt_rand(), TRUE));\n\t\t}\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('increment_string'))\n{\n\t/**\n\t * Add's _1 to a string or increment the ending number to allow _2, _3, etc\n\t *\n\t * @param\tstring\trequired\n\t * @param\tstring\tWhat should the duplicate number be appended with\n\t * @param\tstring\tWhich number should be used for the first dupe increment\n\t * @return\tstring\n\t */\n\tfunction increment_string($str, $separator = '_', $first = 1)\n\t{\n\t\tpreg_match('/(.+)'.preg_quote($separator, '/').'([0-9]+)$/', $str, $match);\n\t\treturn isset($match[2]) ? $match[1].$separator.($match[2] + 1) : $str.$separator.$first;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('alternator'))\n{\n\t/**\n\t * Alternator\n\t *\n\t * Allows strings to be alternated. See docs...\n\t *\n\t * @param\tstring (as many parameters as needed)\n\t * @return\tstring\n\t */\n\tfunction alternator()\n\t{\n\t\tstatic $i;\n\n\t\tif (func_num_args() === 0)\n\t\t{\n\t\t\t$i = 0;\n\t\t\treturn '';\n\t\t}\n\n\t\t$args = func_get_args();\n\t\treturn $args[($i++ % count($args))];\n\t}\n}\n"
  },
  {
    "path": "system/helpers/text_helper.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Text Helpers\n *\n * @package\t\tCodeIgniter\n * @subpackage\tHelpers\n * @category\tHelpers\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/helpers/text_helper.html\n */\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('word_limiter'))\n{\n\t/**\n\t * Word Limiter\n\t *\n\t * Limits a string to X number of words.\n\t *\n\t * @param\tstring\n\t * @param\tint\n\t * @param\tstring\tthe end character. Usually an ellipsis\n\t * @return\tstring\n\t */\n\tfunction word_limiter($str, $limit = 100, $end_char = '&#8230;')\n\t{\n\t\tif (trim($str) === '')\n\t\t{\n\t\t\treturn $str;\n\t\t}\n\n\t\tpreg_match('/^\\s*+(?:\\S++\\s*+){1,'.(int) $limit.'}/', $str, $matches);\n\n\t\tif (strlen($str) === strlen($matches[0]))\n\t\t{\n\t\t\t$end_char = '';\n\t\t}\n\n\t\treturn rtrim($matches[0]).$end_char;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('character_limiter'))\n{\n\t/**\n\t * Character Limiter\n\t *\n\t * Limits the string based on the character count.  Preserves complete words\n\t * so the character count may not be exactly as specified.\n\t *\n\t * @param\tstring\n\t * @param\tint\n\t * @param\tstring\tthe end character. Usually an ellipsis\n\t * @return\tstring\n\t */\n\tfunction character_limiter($str, $n = 500, $end_char = '&#8230;')\n\t{\n\t\tif (mb_strlen($str) < $n)\n\t\t{\n\t\t\treturn $str;\n\t\t}\n\n\t\t// a bit complicated, but faster than preg_replace with \\s+\n\t\t$str = preg_replace('/ {2,}/', ' ', str_replace(array(\"\\r\", \"\\n\", \"\\t\", \"\\v\", \"\\f\"), ' ', $str));\n\n\t\tif (mb_strlen($str) <= $n)\n\t\t{\n\t\t\treturn $str;\n\t\t}\n\n\t\t$out = '';\n\t\tforeach (explode(' ', trim($str)) as $val)\n\t\t{\n\t\t\t$out .= $val.' ';\n\n\t\t\tif (mb_strlen($out) >= $n)\n\t\t\t{\n\t\t\t\t$out = trim($out);\n\t\t\t\treturn (mb_strlen($out) === mb_strlen($str)) ? $out : $out.$end_char;\n\t\t\t}\n\t\t}\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('ascii_to_entities'))\n{\n\t/**\n\t * High ASCII to Entities\n\t *\n\t * Converts high ASCII text and MS Word special characters to character entities\n\t *\n\t * @param\tstring\t$str\n\t * @return\tstring\n\t */\n\tfunction ascii_to_entities($str)\n\t{\n\t\t$out = '';\n\t\t$length = defined('MB_OVERLOAD_STRING')\n\t\t\t? mb_strlen($str, '8bit') - 1\n\t\t\t: strlen($str) - 1;\n\t\tfor ($i = 0, $count = 1, $temp = array(); $i <= $length; $i++)\n\t\t{\n\t\t\t$ordinal = ord($str[$i]);\n\n\t\t\tif ($ordinal < 128)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t\tIf the $temp array has a value but we have moved on, then it seems only\n\t\t\t\t\tfair that we output that entity and restart $temp before continuing. -Paul\n\t\t\t\t*/\n\t\t\t\tif (count($temp) === 1)\n\t\t\t\t{\n\t\t\t\t\t$out .= '&#'.array_shift($temp).';';\n\t\t\t\t\t$count = 1;\n\t\t\t\t}\n\n\t\t\t\t$out .= $str[$i];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (count($temp) === 0)\n\t\t\t\t{\n\t\t\t\t\t$count = ($ordinal < 224) ? 2 : 3;\n\t\t\t\t}\n\n\t\t\t\t$temp[] = $ordinal;\n\n\t\t\t\tif (count($temp) === $count)\n\t\t\t\t{\n\t\t\t\t\t$number = ($count === 3)\n\t\t\t\t\t\t? (($temp[0] % 16) * 4096) + (($temp[1] % 64) * 64) + ($temp[2] % 64)\n\t\t\t\t\t\t: (($temp[0] % 32) * 64) + ($temp[1] % 64);\n\n\t\t\t\t\t$out .= '&#'.$number.';';\n\t\t\t\t\t$count = 1;\n\t\t\t\t\t$temp = array();\n\t\t\t\t}\n\t\t\t\t// If this is the last iteration, just output whatever we have\n\t\t\t\telseif ($i === $length)\n\t\t\t\t{\n\t\t\t\t\t$out .= '&#'.implode(';', $temp).';';\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $out;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('entities_to_ascii'))\n{\n\t/**\n\t * Entities to ASCII\n\t *\n\t * Converts character entities back to ASCII\n\t *\n\t * @param\tstring\n\t * @param\tbool\n\t * @return\tstring\n\t */\n\tfunction entities_to_ascii($str, $all = TRUE)\n\t{\n\t\tif (preg_match_all('/\\&#(\\d+)\\;/', $str, $matches))\n\t\t{\n\t\t\tfor ($i = 0, $s = count($matches[0]); $i < $s; $i++)\n\t\t\t{\n\t\t\t\t$digits = $matches[1][$i];\n\t\t\t\t$out = '';\n\n\t\t\t\tif ($digits < 128)\n\t\t\t\t{\n\t\t\t\t\t$out .= chr($digits);\n\n\t\t\t\t}\n\t\t\t\telseif ($digits < 2048)\n\t\t\t\t{\n\t\t\t\t\t$out .= chr(192 + (($digits - ($digits % 64)) / 64)).chr(128 + ($digits % 64));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$out .= chr(224 + (($digits - ($digits % 4096)) / 4096))\n\t\t\t\t\t\t.chr(128 + ((($digits % 4096) - ($digits % 64)) / 64))\n\t\t\t\t\t\t.chr(128 + ($digits % 64));\n\t\t\t\t}\n\n\t\t\t\t$str = str_replace($matches[0][$i], $out, $str);\n\t\t\t}\n\t\t}\n\n\t\tif ($all)\n\t\t{\n\t\t\treturn str_replace(\n\t\t\t\tarray('&amp;', '&lt;', '&gt;', '&quot;', '&apos;', '&#45;'),\n\t\t\t\tarray('&', '<', '>', '\"', \"'\", '-'),\n\t\t\t\t$str\n\t\t\t);\n\t\t}\n\n\t\treturn $str;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('word_censor'))\n{\n\t/**\n\t * Word Censoring Function\n\t *\n\t * Supply a string and an array of disallowed words and any\n\t * matched words will be converted to #### or to the replacement\n\t * word you've submitted.\n\t *\n\t * @param\tstring\tthe text string\n\t * @param\tstring\tthe array of censored words\n\t * @param\tstring\tthe optional replacement value\n\t * @return\tstring\n\t */\n\tfunction word_censor($str, $censored, $replacement = '')\n\t{\n\t\tif ( ! is_array($censored))\n\t\t{\n\t\t\treturn $str;\n\t\t}\n\n\t\t$str = ' '.$str.' ';\n\n\t\t// \\w, \\b and a few others do not match on a unicode character\n\t\t// set for performance reasons. As a result words like über\n\t\t// will not match on a word boundary. Instead, we'll assume that\n\t\t// a bad word will be bookeneded by any of these characters.\n\t\t$delim = '[-_\\'\\\"`(){}<>\\[\\]|!?@#%&,.:;^~*+=\\/ 0-9\\n\\r\\t]';\n\n\t\tforeach ($censored as $badword)\n\t\t{\n\t\t\t$badword = str_replace('\\*', '\\w*?', preg_quote($badword, '/'));\n\t\t\tif ($replacement !== '')\n\t\t\t{\n\t\t\t\t$str = preg_replace(\n\t\t\t\t\t\"/({$delim})(\".$badword.\")({$delim})/i\",\n\t\t\t\t\t\"\\\\1{$replacement}\\\\3\",\n\t\t\t\t\t$str\n\t\t\t\t);\n\t\t\t}\n\t\t\telseif (preg_match_all(\"/{$delim}(\".$badword.\"){$delim}/i\", $str, $matches, PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE))\n\t\t\t{\n\t\t\t\t$matches = $matches[1];\n\t\t\t\tfor ($i = count($matches) - 1; $i >= 0; $i--)\n\t\t\t\t{\n\t\t\t\t\t$length = strlen($matches[$i][0]);\n\t\t\t\t\t$str = substr_replace(\n\t\t\t\t\t\t$str,\n\t\t\t\t\t\tstr_repeat('#', $length),\n\t\t\t\t\t\t$matches[$i][1],\n\t\t\t\t\t\t$length\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn trim($str);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('highlight_code'))\n{\n\t/**\n\t * Code Highlighter\n\t *\n\t * Colorizes code strings\n\t *\n\t * @param\tstring\tthe text string\n\t * @return\tstring\n\t */\n\tfunction highlight_code($str)\n\t{\n\t\t/* The highlight string function encodes and highlights\n\t\t * brackets so we need them to start raw.\n\t\t *\n\t\t * Also replace any existing PHP tags to temporary markers\n\t\t * so they don't accidentally break the string out of PHP,\n\t\t * and thus, thwart the highlighting.\n\t\t */\n\t\t$str = str_replace(\n\t\t\tarray('&lt;', '&gt;', '<?', '?>', '<%', '%>', '\\\\', '</script>'),\n\t\t\tarray('<', '>', 'phptagopen', 'phptagclose', 'asptagopen', 'asptagclose', 'backslashtmp', 'scriptclose'),\n\t\t\t$str\n\t\t);\n\n\t\t// The highlight_string function requires that the text be surrounded\n\t\t// by PHP tags, which we will remove later\n\t\t$str = highlight_string('<?php '.$str.' ?>', TRUE);\n\n\t\t// Remove our artificially added PHP, and the syntax highlighting that came with it\n\t\t$str = preg_replace(\n\t\t\tarray(\n\t\t\t\t'/<span style=\"color: #([A-Z0-9]+)\">&lt;\\?php(&nbsp;| )/i',\n\t\t\t\t'/(<span style=\"color: #[A-Z0-9]+\">.*?)\\?&gt;<\\/span>\\n<\\/span>\\n<\\/code>/is',\n\t\t\t\t'/<span style=\"color: #[A-Z0-9]+\"\\><\\/span>/i'\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'<span style=\"color: #$1\">',\n\t\t\t\t\"$1</span>\\n</span>\\n</code>\",\n\t\t\t\t''\n\t\t\t),\n\t\t\t$str\n\t\t);\n\n\t\t// Replace our markers back to PHP tags.\n\t\treturn str_replace(\n\t\t\tarray('phptagopen', 'phptagclose', 'asptagopen', 'asptagclose', 'backslashtmp', 'scriptclose'),\n\t\t\tarray('&lt;?', '?&gt;', '&lt;%', '%&gt;', '\\\\', '&lt;/script&gt;'),\n\t\t\t$str\n\t\t);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('highlight_phrase'))\n{\n\t/**\n\t * Phrase Highlighter\n\t *\n\t * Highlights a phrase within a text string\n\t *\n\t * @param\tstring\t$str\t\tthe text string\n\t * @param\tstring\t$phrase\t\tthe phrase you'd like to highlight\n\t * @param\tstring\t$tag_open\tthe openging tag to precede the phrase with\n\t * @param\tstring\t$tag_close\tthe closing tag to end the phrase with\n\t * @return\tstring\n\t */\n\tfunction highlight_phrase($str, $phrase, $tag_open = '<mark>', $tag_close = '</mark>')\n\t{\n\t\treturn ($str !== '' && $phrase !== '')\n\t\t\t? preg_replace('/('.preg_quote($phrase, '/').')/i'.(UTF8_ENABLED ? 'u' : ''), $tag_open.'\\\\1'.$tag_close, $str)\n\t\t\t: $str;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('convert_accented_characters'))\n{\n\t/**\n\t * Convert Accented Foreign Characters to ASCII\n\t *\n\t * @param\tstring\t$str\tInput string\n\t * @return\tstring\n\t */\n\tfunction convert_accented_characters($str)\n\t{\n\t\tstatic $array_from, $array_to;\n\n\t\tif ( ! is_array($array_from))\n\t\t{\n\t\t\tif (file_exists(APPPATH.'config/foreign_chars.php'))\n\t\t\t{\n\t\t\t\tinclude(APPPATH.'config/foreign_chars.php');\n\t\t\t}\n\n\t\t\tif (file_exists(APPPATH.'config/'.ENVIRONMENT.'/foreign_chars.php'))\n\t\t\t{\n\t\t\t\tinclude(APPPATH.'config/'.ENVIRONMENT.'/foreign_chars.php');\n\t\t\t}\n\n\t\t\tif (empty($foreign_characters) OR ! is_array($foreign_characters))\n\t\t\t{\n\t\t\t\t$array_from = array();\n\t\t\t\t$array_to = array();\n\n\t\t\t\treturn $str;\n\t\t\t}\n\n\t\t\t$array_from = array_keys($foreign_characters);\n\t\t\t$array_to = array_values($foreign_characters);\n\t\t}\n\n\t\treturn preg_replace($array_from, $array_to, $str);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('word_wrap'))\n{\n\t/**\n\t * Word Wrap\n\t *\n\t * Wraps text at the specified character. Maintains the integrity of words.\n\t * Anything placed between {unwrap}{/unwrap} will not be word wrapped, nor\n\t * will URLs.\n\t *\n\t * @param\tstring\t$str\t\tthe text string\n\t * @param\tint\t$charlim = 76\tthe number of characters to wrap at\n\t * @return\tstring\n\t */\n\tfunction word_wrap($str, $charlim = 76)\n\t{\n\t\t// Set the character limit\n\t\tis_numeric($charlim) OR $charlim = 76;\n\n\t\t// Reduce multiple spaces\n\t\t$str = preg_replace('| +|', ' ', $str);\n\n\t\t// Standardize newlines\n\t\tif (strpos($str, \"\\r\") !== FALSE)\n\t\t{\n\t\t\t$str = str_replace(array(\"\\r\\n\", \"\\r\"), \"\\n\", $str);\n\t\t}\n\n\t\t// If the current word is surrounded by {unwrap} tags we'll\n\t\t// strip the entire chunk and replace it with a marker.\n\t\t$unwrap = array();\n\t\tif (preg_match_all('|\\{unwrap\\}(.+?)\\{/unwrap\\}|s', $str, $matches))\n\t\t{\n\t\t\tfor ($i = 0, $c = count($matches[0]); $i < $c; $i++)\n\t\t\t{\n\t\t\t\t$unwrap[] = $matches[1][$i];\n\t\t\t\t$str = str_replace($matches[0][$i], '{{unwrapped'.$i.'}}', $str);\n\t\t\t}\n\t\t}\n\n\t\t// Use PHP's native function to do the initial wordwrap.\n\t\t// We set the cut flag to FALSE so that any individual words that are\n\t\t// too long get left alone. In the next step we'll deal with them.\n\t\t$str = wordwrap($str, $charlim, \"\\n\", FALSE);\n\n\t\t// Split the string into individual lines of text and cycle through them\n\t\t$output = '';\n\t\tforeach (explode(\"\\n\", $str) as $line)\n\t\t{\n\t\t\t// Is the line within the allowed character count?\n\t\t\t// If so we'll join it to the output and continue\n\t\t\tif (mb_strlen($line) <= $charlim)\n\t\t\t{\n\t\t\t\t$output .= $line.\"\\n\";\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$temp = '';\n\t\t\twhile (mb_strlen($line) > $charlim)\n\t\t\t{\n\t\t\t\t// If the over-length word is a URL we won't wrap it\n\t\t\t\tif (preg_match('!\\[url.+\\]|://|www\\.!', $line))\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t// Trim the word down\n\t\t\t\t$temp .= mb_substr($line, 0, $charlim - 1);\n\t\t\t\t$line = mb_substr($line, $charlim - 1);\n\t\t\t}\n\n\t\t\t// If $temp contains data it means we had to split up an over-length\n\t\t\t// word into smaller chunks so we'll add it back to our current line\n\t\t\tif ($temp !== '')\n\t\t\t{\n\t\t\t\t$output .= $temp.\"\\n\".$line.\"\\n\";\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$output .= $line.\"\\n\";\n\t\t\t}\n\t\t}\n\n\t\t// Put our markers back\n\t\tif (count($unwrap) > 0)\n\t\t{\n\t\t\tforeach ($unwrap as $key => $val)\n\t\t\t{\n\t\t\t\t$output = str_replace('{{unwrapped'.$key.'}}', $val, $output);\n\t\t\t}\n\t\t}\n\n\t\treturn $output;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('ellipsize'))\n{\n\t/**\n\t * Ellipsize String\n\t *\n\t * This function will strip tags from a string, split it at its max_length and ellipsize\n\t *\n\t * @param\tstring\tstring to ellipsize\n\t * @param\tint\tmax length of string\n\t * @param\tmixed\tint (1|0) or float, .5, .2, etc for position to split\n\t * @param\tstring\tellipsis ; Default '...'\n\t * @return\tstring\tellipsized string\n\t */\n\tfunction ellipsize($str, $max_length, $position = 1, $ellipsis = '&hellip;')\n\t{\n\t\t// Strip tags\n\t\t$str = trim(strip_tags($str));\n\n\t\t// Is the string long enough to ellipsize?\n\t\tif (mb_strlen($str) <= $max_length)\n\t\t{\n\t\t\treturn $str;\n\t\t}\n\n\t\t$beg = mb_substr($str, 0, floor($max_length * $position));\n\t\t$position = ($position > 1) ? 1 : $position;\n\n\t\tif ($position === 1)\n\t\t{\n\t\t\t$end = mb_substr($str, 0, -($max_length - mb_strlen($beg)));\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$end = mb_substr($str, -($max_length - mb_strlen($beg)));\n\t\t}\n\n\t\treturn $beg.$ellipsis.$end;\n\t}\n}\n"
  },
  {
    "path": "system/helpers/typography_helper.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Typography Helpers\n *\n * @package\t\tCodeIgniter\n * @subpackage\tHelpers\n * @category\tHelpers\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/helpers/typography_helper.html\n */\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('nl2br_except_pre'))\n{\n\t/**\n\t * Convert newlines to HTML line breaks except within PRE tags\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tfunction nl2br_except_pre($str)\n\t{\n\t\t$CI =& get_instance();\n\t\t$CI->load->library('typography');\n\t\treturn $CI->typography->nl2br_except_pre($str);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('auto_typography'))\n{\n\t/**\n\t * Auto Typography Wrapper Function\n\t *\n\t * @param\tstring\t$str\n\t * @param\tbool\t$reduce_linebreaks = FALSE\twhether to reduce multiple instances of double newlines to two\n\t * @return\tstring\n\t */\n\tfunction auto_typography($str, $reduce_linebreaks = FALSE)\n\t{\n\t\t$CI =& get_instance();\n\t\t$CI->load->library('typography');\n\t\treturn $CI->typography->auto_typography($str, $reduce_linebreaks);\n\t}\n}\n\n// --------------------------------------------------------------------\n\nif ( ! function_exists('entity_decode'))\n{\n\t/**\n\t * HTML Entities Decode\n\t *\n\t * This function is a replacement for html_entity_decode()\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tfunction entity_decode($str, $charset = NULL)\n\t{\n\t\treturn get_instance()->security->entity_decode($str, $charset);\n\t}\n}\n"
  },
  {
    "path": "system/helpers/url_helper.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter URL Helpers\n *\n * @package\t\tCodeIgniter\n * @subpackage\tHelpers\n * @category\tHelpers\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/helpers/url_helper.html\n */\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('site_url'))\n{\n\t/**\n\t * Site URL\n\t *\n\t * Create a local URL based on your basepath. Segments can be passed via the\n\t * first parameter either as a string or an array.\n\t *\n\t * @param\tstring\t$uri\n\t * @param\tstring\t$protocol\n\t * @return\tstring\n\t */\n\tfunction site_url($uri = '', $protocol = NULL)\n\t{\n\t\treturn get_instance()->config->site_url($uri, $protocol);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('base_url'))\n{\n\t/**\n\t * Base URL\n\t *\n\t * Create a local URL based on your basepath.\n\t * Segments can be passed in as a string or an array, same as site_url\n\t * or a URL to a file can be passed in, e.g. to an image file.\n\t *\n\t * @param\tstring\t$uri\n\t * @param\tstring\t$protocol\n\t * @return\tstring\n\t */\n\tfunction base_url($uri = '', $protocol = NULL)\n\t{\n\t\treturn get_instance()->config->base_url($uri, $protocol);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('current_url'))\n{\n\t/**\n\t * Current URL\n\t *\n\t * Returns the full URL (including segments) of the page where this\n\t * function is placed\n\t *\n\t * @return\tstring\n\t */\n\tfunction current_url()\n\t{\n\t\t$CI =& get_instance();\n\t\treturn $CI->config->site_url($CI->uri->uri_string());\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('uri_string'))\n{\n\t/**\n\t * URL String\n\t *\n\t * Returns the URI segments.\n\t *\n\t * @return\tstring\n\t */\n\tfunction uri_string()\n\t{\n\t\treturn get_instance()->uri->uri_string();\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('index_page'))\n{\n\t/**\n\t * Index page\n\t *\n\t * Returns the \"index_page\" from your config file\n\t *\n\t * @return\tstring\n\t */\n\tfunction index_page()\n\t{\n\t\treturn get_instance()->config->item('index_page');\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('anchor'))\n{\n\t/**\n\t * Anchor Link\n\t *\n\t * Creates an anchor based on the local URL.\n\t *\n\t * @param\tstring\tthe URL\n\t * @param\tstring\tthe link title\n\t * @param\tmixed\tany attributes\n\t * @return\tstring\n\t */\n\tfunction anchor($uri = '', $title = '', $attributes = '')\n\t{\n\t\t$title = (string) $title;\n\n\t\t$site_url = is_array($uri)\n\t\t\t? site_url($uri)\n\t\t\t: (preg_match('#^(\\w+:)?//#i', $uri) ? $uri : site_url($uri));\n\n\t\tif ($title === '')\n\t\t{\n\t\t\t$title = $site_url;\n\t\t}\n\n\t\tif ($attributes !== '')\n\t\t{\n\t\t\t$attributes = _stringify_attributes($attributes);\n\t\t}\n\n\t\treturn '<a href=\"'.$site_url.'\"'.$attributes.'>'.$title.'</a>';\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('anchor_popup'))\n{\n\t/**\n\t * Anchor Link - Pop-up version\n\t *\n\t * Creates an anchor based on the local URL. The link\n\t * opens a new window based on the attributes specified.\n\t *\n\t * @param\tstring\tthe URL\n\t * @param\tstring\tthe link title\n\t * @param\tmixed\tany attributes\n\t * @return\tstring\n\t */\n\tfunction anchor_popup($uri = '', $title = '', $attributes = FALSE)\n\t{\n\t\t$title = (string) $title;\n\t\t$site_url = preg_match('#^(\\w+:)?//#i', $uri) ? $uri : site_url($uri);\n\n\t\tif ($title === '')\n\t\t{\n\t\t\t$title = $site_url;\n\t\t}\n\n\t\tif ($attributes === FALSE)\n\t\t{\n\t\t\treturn '<a href=\"'.$site_url.'\" onclick=\"window.open(\\''.$site_url.\"', '_blank'); return false;\\\">\".$title.'</a>';\n\t\t}\n\n\t\tif ( ! is_array($attributes))\n\t\t{\n\t\t\t$attributes = array($attributes);\n\n\t\t\t// Ref: https://www.w3schools.com/jsref/met_win_open.asp\n\t\t\t$window_name = '_blank';\n\t\t}\n\t\telseif ( ! empty($attributes['window_name']))\n\t\t{\n\t\t\t$window_name = $attributes['window_name'];\n\t\t\tunset($attributes['window_name']);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$window_name = '_blank';\n\t\t}\n\n\t\tforeach (array('width' => '800', 'height' => '600', 'scrollbars' => 'yes', 'menubar' => 'no', 'status' => 'yes', 'resizable' => 'yes', 'screenx' => '0', 'screeny' => '0') as $key => $val)\n\t\t{\n\t\t\t$atts[$key] = isset($attributes[$key]) ? $attributes[$key] : $val;\n\t\t\tunset($attributes[$key]);\n\t\t}\n\n\t\t$attributes = _stringify_attributes($attributes);\n\n\t\treturn '<a href=\"'.$site_url\n\t\t\t.'\" onclick=\"window.open(\\''.$site_url.\"', '\".$window_name.\"', '\"._stringify_attributes($atts, TRUE).\"'); return false;\\\"\"\n\t\t\t.$attributes.'>'.$title.'</a>';\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('mailto'))\n{\n\t/**\n\t * Mailto Link\n\t *\n\t * @param\tstring\tthe email address\n\t * @param\tstring\tthe link title\n\t * @param\tmixed\tany attributes\n\t * @return\tstring\n\t */\n\tfunction mailto($email, $title = '', $attributes = '')\n\t{\n\t\t$title = (string) $title;\n\n\t\tif ($title === '')\n\t\t{\n\t\t\t$title = $email;\n\t\t}\n\n\t\treturn '<a href=\"mailto:'.$email.'\"'._stringify_attributes($attributes).'>'.$title.'</a>';\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('safe_mailto'))\n{\n\t/**\n\t * Encoded Mailto Link\n\t *\n\t * Create a spam-protected mailto link written in Javascript\n\t *\n\t * @param\tstring\tthe email address\n\t * @param\tstring\tthe link title\n\t * @param\tmixed\tany attributes\n\t * @return\tstring\n\t */\n\tfunction safe_mailto($email, $title = '', $attributes = '')\n\t{\n\t\t$title = (string) $title;\n\n\t\tif ($title === '')\n\t\t{\n\t\t\t$title = $email;\n\t\t}\n\n\t\t$x = str_split('<a href=\"mailto:', 1);\n\n\t\tfor ($i = 0, $l = strlen($email); $i < $l; $i++)\n\t\t{\n\t\t\t$x[] = '|'.ord($email[$i]);\n\t\t}\n\n\t\t$x[] = '\"';\n\n\t\tif ($attributes !== '')\n\t\t{\n\t\t\tif (is_array($attributes))\n\t\t\t{\n\t\t\t\tforeach ($attributes as $key => $val)\n\t\t\t\t{\n\t\t\t\t\t$x[] = ' '.$key.'=\"';\n\t\t\t\t\tfor ($i = 0, $l = strlen($val); $i < $l; $i++)\n\t\t\t\t\t{\n\t\t\t\t\t\t$x[] = '|'.ord($val[$i]);\n\t\t\t\t\t}\n\t\t\t\t\t$x[] = '\"';\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor ($i = 0, $l = strlen($attributes); $i < $l; $i++)\n\t\t\t\t{\n\t\t\t\t\t$x[] = $attributes[$i];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$x[] = '>';\n\n\t\t$temp = array();\n\t\tfor ($i = 0, $l = strlen($title); $i < $l; $i++)\n\t\t{\n\t\t\t$ordinal = ord($title[$i]);\n\n\t\t\tif ($ordinal < 128)\n\t\t\t{\n\t\t\t\t$x[] = '|'.$ordinal;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (count($temp) === 0)\n\t\t\t\t{\n\t\t\t\t\t$count = ($ordinal < 224) ? 2 : 3;\n\t\t\t\t}\n\n\t\t\t\t$temp[] = $ordinal;\n\t\t\t\tif (count($temp) === $count)\n\t\t\t\t{\n\t\t\t\t\t$number = ($count === 3)\n\t\t\t\t\t\t\t? (($temp[0] % 16) * 4096) + (($temp[1] % 64) * 64) + ($temp[2] % 64)\n\t\t\t\t\t\t\t: (($temp[0] % 32) * 64) + ($temp[1] % 64);\n\t\t\t\t\t$x[] = '|'.$number;\n\t\t\t\t\t$count = 1;\n\t\t\t\t\t$temp = array();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$x[] = '<'; $x[] = '/'; $x[] = 'a'; $x[] = '>';\n\n\t\t$x = array_reverse($x);\n\n\t\t$output = \"<script type=\\\"text/javascript\\\">\\n\"\n\t\t\t.\"\\t//<![CDATA[\\n\"\n\t\t\t.\"\\tvar l=new Array();\\n\";\n\n\t\tfor ($i = 0, $c = count($x); $i < $c; $i++)\n\t\t{\n\t\t\t$output .= \"\\tl[\".$i.\"] = '\".$x[$i].\"';\\n\";\n\t\t}\n\n\t\t$output .= \"\\n\\tfor (var i = l.length-1; i >= 0; i=i-1) {\\n\"\n\t\t\t.\"\\t\\tif (l[i].substring(0, 1) === '|') document.write(\\\"&#\\\"+unescape(l[i].substring(1))+\\\";\\\");\\n\"\n\t\t\t.\"\\t\\telse document.write(unescape(l[i]));\\n\"\n\t\t\t.\"\\t}\\n\"\n\t\t\t.\"\\t//]]>\\n\"\n\t\t\t.'</script>';\n\n\t\treturn $output;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('auto_link'))\n{\n\t/**\n\t * Auto-linker\n\t *\n\t * Automatically links URL and Email addresses.\n\t * Note: There's a bit of extra code here to deal with\n\t * URLs or emails that end in a period. We'll strip these\n\t * off and add them after the link.\n\t *\n\t * @param\tstring\tthe string\n\t * @param\tstring\tthe type: email, url, or both\n\t * @param\tbool\twhether to create pop-up links\n\t * @return\tstring\n\t */\n\tfunction auto_link($str, $type = 'both', $popup = FALSE)\n\t{\n\t\t// Find and replace any URLs.\n\t\tif ($type !== 'email' && preg_match_all('#(\\w*://|www\\.)[a-z0-9]+(-+[a-z0-9]+)*(\\.[a-z0-9]+(-+[a-z0-9]+)*)+(/([^\\s()<>;]+\\w)?/?)?#i', $str, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER))\n\t\t{\n\t\t\t// Set our target HTML if using popup links.\n\t\t\t$target = ($popup) ? ' target=\"_blank\" rel=\"noopener\"' : '';\n\n\t\t\t// We process the links in reverse order (last -> first) so that\n\t\t\t// the returned string offsets from preg_match_all() are not\n\t\t\t// moved as we add more HTML.\n\t\t\tforeach (array_reverse($matches) as $match)\n\t\t\t{\n\t\t\t\t// $match[0] is the matched string/link\n\t\t\t\t// $match[1] is either a protocol prefix or 'www.'\n\t\t\t\t//\n\t\t\t\t// With PREG_OFFSET_CAPTURE, both of the above is an array,\n\t\t\t\t// where the actual value is held in [0] and its offset at the [1] index.\n\t\t\t\t$a = '<a href=\"'.(strpos($match[1][0], '/') ? '' : 'http://').$match[0][0].'\"'.$target.'>'.$match[0][0].'</a>';\n\t\t\t\t$str = substr_replace($str, $a, $match[0][1], strlen($match[0][0]));\n\t\t\t}\n\t\t}\n\n\t\t// Find and replace any emails.\n\t\tif ($type !== 'url' && preg_match_all('#([\\w\\.\\-\\+]+@[a-z0-9\\-]+\\.[a-z0-9\\-\\.]+[^[:punct:]\\s])#i', $str, $matches, PREG_OFFSET_CAPTURE))\n\t\t{\n\t\t\tforeach (array_reverse($matches[0]) as $match)\n\t\t\t{\n\t\t\t\tif (filter_var($match[0], FILTER_VALIDATE_EMAIL) !== FALSE)\n\t\t\t\t{\n\t\t\t\t\t$str = substr_replace($str, safe_mailto($match[0]), $match[1], strlen($match[0]));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $str;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('prep_url'))\n{\n\t/**\n\t * Prep URL\n\t *\n\t * Simply adds the http:// part if no scheme is included\n\t *\n\t * @param\tstring\tthe URL\n\t * @return\tstring\n\t */\n\tfunction prep_url($str = '')\n\t{\n\t\tif ($str === '')\n\t\t{\n\t\t\treturn '';\n\t\t}\n\n\t\t$url = parse_url($str);\n\n\t\tif ( ! $url OR ! isset($url['scheme']))\n\t\t{\n\t\t\treturn 'http://'.$str;\n\t\t}\n\n\t\treturn $str;\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('url_title'))\n{\n\t/**\n\t * Create URL Title\n\t *\n\t * Takes a \"title\" string as input and creates a\n\t * human-friendly URL string with a \"separator\" string\n\t * as the word separator.\n\t *\n\t * @param\tstring\t$str\t\tInput string\n\t * @param\tstring\t$separator\tWord separator (usually '-' or '_')\n\t * @param\tbool\t$lowercase\tWhether to transform the output string to lowercase\n\t * @return\tstring\n\t */\n\tfunction url_title($str, $separator = '-', $lowercase = FALSE)\n\t{\n\t\t$q_separator = preg_quote($separator, '#');\n\n\t\t$trans = array(\n\t\t\t'&.+?;'\t\t\t=> '',\n\t\t\t'[^\\w\\d _-]'\t\t=> '',\n\t\t\t'\\s+'\t\t\t=> $separator,\n\t\t\t'('.$q_separator.')+'\t=> $separator,\n\t\t);\n\n\t\t$str = strip_tags($str);\n\t\tforeach ($trans as $key => $val)\n\t\t{\n\t\t\t$str = preg_replace('#'.$key.'#i'.(UTF8_ENABLED ? 'u' : ''), $val, $str);\n\t\t}\n\n\t\tif ($lowercase === TRUE)\n\t\t{\n\t\t\t$str = strtolower($str);\n\t\t}\n\n\t\treturn trim(trim($str, $separator));\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('redirect'))\n{\n\t/**\n\t * Header Redirect\n\t *\n\t * Header redirect in two flavors\n\t * For very fine grained control over headers, you could use the Output\n\t * Library's set_header() function.\n\t *\n\t * @param\tstring\t$uri\tURL\n\t * @param\tstring\t$method\tRedirect method\n\t *\t\t\t'auto', 'location' or 'refresh'\n\t * @param\tint\t$code\tHTTP Response status code\n\t * @return\tvoid\n\t */\n\tfunction redirect($uri = '', $method = 'auto', $code = NULL)\n\t{\n\t\tif ( ! preg_match('#^(\\w+:)?//#i', $uri))\n\t\t{\n\t\t\t$uri = site_url($uri);\n\t\t}\n\n\t\t// IIS environment likely? Use 'refresh' for better compatibility\n\t\tif ($method === 'auto' && isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== FALSE)\n\t\t{\n\t\t\t$method = 'refresh';\n\t\t}\n\t\telseif ($method !== 'refresh' && (empty($code) OR ! is_numeric($code)))\n\t\t{\n\t\t\tif (isset($_SERVER['SERVER_PROTOCOL'], $_SERVER['REQUEST_METHOD']) && $_SERVER['SERVER_PROTOCOL'] === 'HTTP/1.1')\n\t\t\t{\n\t\t\t\t$code = ($_SERVER['REQUEST_METHOD'] !== 'GET')\n\t\t\t\t\t? 303\t// reference: https://en.wikipedia.org/wiki/Post/Redirect/Get\n\t\t\t\t\t: 307;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$code = 302;\n\t\t\t}\n\t\t}\n\n\t\tswitch ($method)\n\t\t{\n\t\t\tcase 'refresh':\n\t\t\t\theader('Refresh:0;url='.$uri);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\theader('Location: '.$uri, TRUE, $code);\n\t\t\t\tbreak;\n\t\t}\n\t\texit;\n\t}\n}\n"
  },
  {
    "path": "system/helpers/xml_helper.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter XML Helpers\n *\n * @package\t\tCodeIgniter\n * @subpackage\tHelpers\n * @category\tHelpers\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/helpers/xml_helper.html\n */\n\n// ------------------------------------------------------------------------\n\nif ( ! function_exists('xml_convert'))\n{\n\t/**\n\t * Convert Reserved XML characters to Entities\n\t *\n\t * @param\tstring\n\t * @param\tbool\n\t * @return\tstring\n\t */\n\tfunction xml_convert($str, $protect_all = FALSE)\n\t{\n\t\t$temp = '__TEMP_AMPERSANDS__';\n\n\t\t// Replace entities to temporary markers so that\n\t\t// ampersands won't get messed up\n\t\t$str = preg_replace('/&#(\\d+);/', $temp.'\\\\1;', $str);\n\n\t\tif ($protect_all === TRUE)\n\t\t{\n\t\t\t$str = preg_replace('/&(\\w+);/', $temp.'\\\\1;', $str);\n\t\t}\n\n\t\t$str = str_replace(\n\t\t\tarray('&', '<', '>', '\"', \"'\", '-'),\n\t\t\tarray('&amp;', '&lt;', '&gt;', '&quot;', '&apos;', '&#45;'),\n\t\t\t$str\n\t\t);\n\n\t\t// Decode the temp markers back to entities\n\t\t$str = preg_replace('/'.$temp.'(\\d+);/', '&#\\\\1;', $str);\n\n\t\tif ($protect_all === TRUE)\n\t\t{\n\t\t\treturn preg_replace('/'.$temp.'(\\w+);/', '&\\\\1;', $str);\n\t\t}\n\n\t\treturn $str;\n\t}\n}\n"
  },
  {
    "path": "system/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/language/english/calendar_lang.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n$lang['cal_su'] = 'Su';\n$lang['cal_mo'] = 'Mo';\n$lang['cal_tu'] = 'Tu';\n$lang['cal_we'] = 'We';\n$lang['cal_th'] = 'Th';\n$lang['cal_fr'] = 'Fr';\n$lang['cal_sa'] = 'Sa';\n$lang['cal_sun'] = 'Sun';\n$lang['cal_mon'] = 'Mon';\n$lang['cal_tue'] = 'Tue';\n$lang['cal_wed'] = 'Wed';\n$lang['cal_thu'] = 'Thu';\n$lang['cal_fri'] = 'Fri';\n$lang['cal_sat'] = 'Sat';\n$lang['cal_sunday'] = 'Sunday';\n$lang['cal_monday'] = 'Monday';\n$lang['cal_tuesday'] = 'Tuesday';\n$lang['cal_wednesday'] = 'Wednesday';\n$lang['cal_thursday'] = 'Thursday';\n$lang['cal_friday'] = 'Friday';\n$lang['cal_saturday'] = 'Saturday';\n$lang['cal_jan'] = 'Jan';\n$lang['cal_feb'] = 'Feb';\n$lang['cal_mar'] = 'Mar';\n$lang['cal_apr'] = 'Apr';\n$lang['cal_may'] = 'May';\n$lang['cal_jun'] = 'Jun';\n$lang['cal_jul'] = 'Jul';\n$lang['cal_aug'] = 'Aug';\n$lang['cal_sep'] = 'Sep';\n$lang['cal_oct'] = 'Oct';\n$lang['cal_nov'] = 'Nov';\n$lang['cal_dec'] = 'Dec';\n$lang['cal_january'] = 'January';\n$lang['cal_february'] = 'February';\n$lang['cal_march'] = 'March';\n$lang['cal_april'] = 'April';\n$lang['cal_mayl'] = 'May';\n$lang['cal_june'] = 'June';\n$lang['cal_july'] = 'July';\n$lang['cal_august'] = 'August';\n$lang['cal_september'] = 'September';\n$lang['cal_october'] = 'October';\n$lang['cal_november'] = 'November';\n$lang['cal_december'] = 'December';\n"
  },
  {
    "path": "system/language/english/date_lang.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n$lang['date_year'] = 'Year';\n$lang['date_years'] = 'Years';\n$lang['date_month'] = 'Month';\n$lang['date_months'] = 'Months';\n$lang['date_week'] = 'Week';\n$lang['date_weeks'] = 'Weeks';\n$lang['date_day'] = 'Day';\n$lang['date_days'] = 'Days';\n$lang['date_hour'] = 'Hour';\n$lang['date_hours'] = 'Hours';\n$lang['date_minute'] = 'Minute';\n$lang['date_minutes'] = 'Minutes';\n$lang['date_second'] = 'Second';\n$lang['date_seconds'] = 'Seconds';\n\n$lang['UM12']\t= '(UTC -12:00) Baker/Howland Island';\n$lang['UM11']\t= '(UTC -11:00) Niue';\n$lang['UM10']\t= '(UTC -10:00) Hawaii-Aleutian Standard Time, Cook Islands, Tahiti';\n$lang['UM95']\t= '(UTC -9:30) Marquesas Islands';\n$lang['UM9']\t= '(UTC -9:00) Alaska Standard Time, Gambier Islands';\n$lang['UM8']\t= '(UTC -8:00) Pacific Standard Time, Clipperton Island';\n$lang['UM7']\t= '(UTC -7:00) Mountain Standard Time';\n$lang['UM6']\t= '(UTC -6:00) Central Standard Time';\n$lang['UM5']\t= '(UTC -5:00) Eastern Standard Time, Western Caribbean Standard Time';\n$lang['UM45']\t= '(UTC -4:30) Venezuelan Standard Time';\n$lang['UM4']\t= '(UTC -4:00) Atlantic Standard Time, Eastern Caribbean Standard Time';\n$lang['UM35']\t= '(UTC -3:30) Newfoundland Standard Time';\n$lang['UM3']\t= '(UTC -3:00) Argentina, Brazil, French Guiana, Uruguay';\n$lang['UM2']\t= '(UTC -2:00) South Georgia/South Sandwich Islands';\n$lang['UM1']\t= '(UTC -1:00) Azores, Cape Verde Islands';\n$lang['UTC']\t= '(UTC) Greenwich Mean Time, Western European Time';\n$lang['UP1']\t= '(UTC +1:00) Central European Time, West Africa Time';\n$lang['UP2']\t= '(UTC +2:00) Central Africa Time, Eastern European Time, Kaliningrad Time';\n$lang['UP3']\t= '(UTC +3:00) Moscow Time, East Africa Time, Arabia Standard Time';\n$lang['UP35']\t= '(UTC +3:30) Iran Standard Time';\n$lang['UP4']\t= '(UTC +4:00) Azerbaijan Standard Time, Samara Time';\n$lang['UP45']\t= '(UTC +4:30) Afghanistan';\n$lang['UP5']\t= '(UTC +5:00) Pakistan Standard Time, Yekaterinburg Time';\n$lang['UP55']\t= '(UTC +5:30) Indian Standard Time, Sri Lanka Time';\n$lang['UP575']\t= '(UTC +5:45) Nepal Time';\n$lang['UP6']\t= '(UTC +6:00) Bangladesh Standard Time, Bhutan Time, Omsk Time';\n$lang['UP65']\t= '(UTC +6:30) Cocos Islands, Myanmar';\n$lang['UP7']\t= '(UTC +7:00) Krasnoyarsk Time, Cambodia, Laos, Thailand, Vietnam';\n$lang['UP8']\t= '(UTC +8:00) Australian Western Standard Time, Beijing Time, Irkutsk Time';\n$lang['UP875']\t= '(UTC +8:45) Australian Central Western Standard Time';\n$lang['UP9']\t= '(UTC +9:00) Japan Standard Time, Korea Standard Time, Yakutsk Time';\n$lang['UP95']\t= '(UTC +9:30) Australian Central Standard Time';\n$lang['UP10']\t= '(UTC +10:00) Australian Eastern Standard Time, Vladivostok Time';\n$lang['UP105']\t= '(UTC +10:30) Lord Howe Island';\n$lang['UP11']\t= '(UTC +11:00) Srednekolymsk Time, Solomon Islands, Vanuatu';\n$lang['UP115']\t= '(UTC +11:30) Norfolk Island';\n$lang['UP12']\t= '(UTC +12:00) Fiji, Gilbert Islands, Kamchatka Time, New Zealand Standard Time';\n$lang['UP1275']\t= '(UTC +12:45) Chatham Islands Standard Time';\n$lang['UP13']\t= '(UTC +13:00) Samoa Time Zone, Phoenix Islands Time, Tonga';\n$lang['UP14']\t= '(UTC +14:00) Line Islands';\n"
  },
  {
    "path": "system/language/english/db_lang.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n$lang['db_invalid_connection_str'] = 'Unable to determine the database settings based on the connection string you submitted.';\n$lang['db_unable_to_connect'] = 'Unable to connect to your database server using the provided settings.';\n$lang['db_unable_to_select'] = 'Unable to select the specified database: %s';\n$lang['db_unable_to_create'] = 'Unable to create the specified database: %s';\n$lang['db_invalid_query'] = 'The query you submitted is not valid.';\n$lang['db_must_set_table'] = 'You must set the database table to be used with your query.';\n$lang['db_must_use_set'] = 'You must use the \"set\" method to update an entry.';\n$lang['db_must_use_index'] = 'You must specify an index to match on for batch updates.';\n$lang['db_batch_missing_index'] = 'One or more rows submitted for batch updating is missing the specified index.';\n$lang['db_must_use_where'] = 'Updates are not allowed unless they contain a \"where\" clause.';\n$lang['db_del_must_use_where'] = 'Deletes are not allowed unless they contain a \"where\" or \"like\" clause.';\n$lang['db_field_param_missing'] = 'To fetch fields requires the name of the table as a parameter.';\n$lang['db_unsupported_function'] = 'This feature is not available for the database you are using.';\n$lang['db_transaction_failure'] = 'Transaction failure: Rollback performed.';\n$lang['db_unable_to_drop'] = 'Unable to drop the specified database.';\n$lang['db_unsupported_feature'] = 'Unsupported feature of the database platform you are using.';\n$lang['db_unsupported_compression'] = 'The file compression format you chose is not supported by your server.';\n$lang['db_filepath_error'] = 'Unable to write data to the file path you have submitted.';\n$lang['db_invalid_cache_path'] = 'The cache path you submitted is not valid or writable.';\n$lang['db_table_name_required'] = 'A table name is required for that operation.';\n$lang['db_column_name_required'] = 'A column name is required for that operation.';\n$lang['db_column_definition_required'] = 'A column definition is required for that operation.';\n$lang['db_unable_to_set_charset'] = 'Unable to set client connection character set: %s';\n$lang['db_error_heading'] = 'A Database Error Occurred';\n"
  },
  {
    "path": "system/language/english/email_lang.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n$lang['email_must_be_array'] = 'The email validation method must be passed an array.';\n$lang['email_invalid_address'] = 'Invalid email address: %s';\n$lang['email_attachment_missing'] = 'Unable to locate the following email attachment: %s';\n$lang['email_attachment_unreadable'] = 'Unable to open this attachment: %s';\n$lang['email_no_from'] = 'Cannot send mail with no \"From\" header.';\n$lang['email_no_recipients'] = 'You must include recipients: To, Cc, or Bcc';\n$lang['email_send_failure_phpmail'] = 'Unable to send email using PHP mail(). Your server might not be configured to send mail using this method.';\n$lang['email_send_failure_sendmail'] = 'Unable to send email using PHP Sendmail. Your server might not be configured to send mail using this method.';\n$lang['email_send_failure_smtp'] = 'Unable to send email using PHP SMTP. Your server might not be configured to send mail using this method.';\n$lang['email_sent'] = 'Your message has been successfully sent using the following protocol: %s';\n$lang['email_no_socket'] = 'Unable to open a socket to Sendmail. Please check settings.';\n$lang['email_no_hostname'] = 'You did not specify a SMTP hostname.';\n$lang['email_smtp_error'] = 'The following SMTP error was encountered: %s';\n$lang['email_no_smtp_unpw'] = 'Error: You must assign a SMTP username and password.';\n$lang['email_failed_smtp_login'] = 'Failed to send AUTH LOGIN command. Error: %s';\n$lang['email_smtp_auth_un'] = 'Failed to authenticate username. Error: %s';\n$lang['email_smtp_auth_pw'] = 'Failed to authenticate password. Error: %s';\n$lang['email_smtp_data_failure'] = 'Unable to send data: %s';\n$lang['email_exit_status'] = 'Exit status code: %s';\n"
  },
  {
    "path": "system/language/english/form_validation_lang.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n$lang['form_validation_required']\t\t= 'The {field} field is required.';\n$lang['form_validation_isset']\t\t\t= 'The {field} field must have a value.';\n$lang['form_validation_valid_email']\t\t= 'The {field} field must contain a valid email address.';\n$lang['form_validation_valid_emails']\t\t= 'The {field} field must contain all valid email addresses.';\n$lang['form_validation_valid_url']\t\t= 'The {field} field must contain a valid URL.';\n$lang['form_validation_valid_ip']\t\t= 'The {field} field must contain a valid IP.';\n$lang['form_validation_valid_mac']\t\t= 'The {field} field must contain a valid MAC.';\n$lang['form_validation_valid_base64']\t\t= 'The {field} field must contain a valid Base64 string.';\n$lang['form_validation_min_length']\t\t= 'The {field} field must be at least {param} characters in length.';\n$lang['form_validation_max_length']\t\t= 'The {field} field cannot exceed {param} characters in length.';\n$lang['form_validation_exact_length']\t\t= 'The {field} field must be exactly {param} characters in length.';\n$lang['form_validation_alpha']\t\t\t= 'The {field} field may only contain alphabetical characters.';\n$lang['form_validation_alpha_numeric']\t\t= 'The {field} field may only contain alpha-numeric characters.';\n$lang['form_validation_alpha_numeric_spaces']\t= 'The {field} field may only contain alpha-numeric characters and spaces.';\n$lang['form_validation_alpha_dash']\t\t= 'The {field} field may only contain alpha-numeric characters, underscores, and dashes.';\n$lang['form_validation_numeric']\t\t= 'The {field} field must contain only numbers.';\n$lang['form_validation_is_numeric']\t\t= 'The {field} field must contain only numeric characters.';\n$lang['form_validation_integer']\t\t= 'The {field} field must contain an integer.';\n$lang['form_validation_regex_match']\t\t= 'The {field} field is not in the correct format.';\n$lang['form_validation_matches']\t\t= 'The {field} field does not match the {param} field.';\n$lang['form_validation_differs']\t\t= 'The {field} field must differ from the {param} field.';\n$lang['form_validation_is_unique'] \t\t= 'The {field} field must contain a unique value.';\n$lang['form_validation_is_natural']\t\t= 'The {field} field must only contain digits.';\n$lang['form_validation_is_natural_no_zero']\t= 'The {field} field must only contain digits and must be greater than zero.';\n$lang['form_validation_decimal']\t\t= 'The {field} field must contain a decimal number.';\n$lang['form_validation_less_than']\t\t= 'The {field} field must contain a number less than {param}.';\n$lang['form_validation_less_than_equal_to']\t= 'The {field} field must contain a number less than or equal to {param}.';\n$lang['form_validation_greater_than']\t\t= 'The {field} field must contain a number greater than {param}.';\n$lang['form_validation_greater_than_equal_to']\t= 'The {field} field must contain a number greater than or equal to {param}.';\n$lang['form_validation_error_message_not_set']\t= 'Unable to access an error message corresponding to your field name {field}.';\n$lang['form_validation_in_list']\t\t= 'The {field} field must be one of: {param}.';\n"
  },
  {
    "path": "system/language/english/ftp_lang.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n$lang['ftp_no_connection']\t\t= 'Unable to locate a valid connection ID. Please make sure you are connected before performing any file routines.';\n$lang['ftp_unable_to_connect']\t\t= 'Unable to connect to your FTP server using the supplied hostname.';\n$lang['ftp_unable_to_login']\t\t= 'Unable to login to your FTP server. Please check your username and password.';\n$lang['ftp_unable_to_mkdir']\t\t= 'Unable to create the directory you have specified.';\n$lang['ftp_unable_to_changedir']\t= 'Unable to change directories.';\n$lang['ftp_unable_to_chmod']\t\t= 'Unable to set file permissions. Please check your path.';\n$lang['ftp_unable_to_upload']\t\t= 'Unable to upload the specified file. Please check your path.';\n$lang['ftp_unable_to_download']\t\t= 'Unable to download the specified file. Please check your path.';\n$lang['ftp_no_source_file']\t\t= 'Unable to locate the source file. Please check your path.';\n$lang['ftp_unable_to_rename']\t\t= 'Unable to rename the file.';\n$lang['ftp_unable_to_delete']\t\t= 'Unable to delete the file.';\n$lang['ftp_unable_to_move']\t\t= 'Unable to move the file. Please make sure the destination directory exists.';\n"
  },
  {
    "path": "system/language/english/imglib_lang.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n$lang['imglib_source_image_required'] = 'You must specify a source image in your preferences.';\n$lang['imglib_gd_required'] = 'The GD image library is required for this feature.';\n$lang['imglib_gd_required_for_props'] = 'Your server must support the GD image library in order to determine the image properties.';\n$lang['imglib_unsupported_imagecreate'] = 'Your server does not support the GD function required to process this type of image.';\n$lang['imglib_gif_not_supported'] = 'GIF images are often not supported due to licensing restrictions. You may have to use JPG or PNG images instead.';\n$lang['imglib_jpg_not_supported'] = 'JPG images are not supported.';\n$lang['imglib_png_not_supported'] = 'PNG images are not supported.';\n$lang['imglib_webp_not_supported'] = 'WEBP images are not supported.';\n$lang['imglib_jpg_or_png_required'] = 'The image resize protocol specified in your preferences only works with JPEG or PNG image types.';\n$lang['imglib_copy_error'] = 'An error was encountered while attempting to replace the file. Please make sure your file directory is writable.';\n$lang['imglib_rotate_unsupported'] = 'Image rotation does not appear to be supported by your server.';\n$lang['imglib_libpath_invalid'] = 'The path to your image library is not correct. Please set the correct path in your image preferences.';\n$lang['imglib_image_process_failed'] = 'Image processing failed. Please verify that your server supports the chosen protocol and that the path to your image library is correct.';\n$lang['imglib_rotation_angle_required'] = 'An angle of rotation is required to rotate the image.';\n$lang['imglib_invalid_path'] = 'The path to the image is not correct.';\n$lang['imglib_invalid_image'] = 'The provided image is not valid.';\n$lang['imglib_copy_failed'] = 'The image copy routine failed.';\n$lang['imglib_missing_font'] = 'Unable to find a font to use.';\n$lang['imglib_save_failed'] = 'Unable to save the image. Please make sure the image and file directory are writable.';\n"
  },
  {
    "path": "system/language/english/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/language/english/migration_lang.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n$lang['migration_none_found'] = 'No migrations were found.';\n$lang['migration_not_found'] = 'No migration could be found with the version number: %s.';\n$lang['migration_sequence_gap'] = 'There is a gap in the migration sequence near version number: %s.';\n$lang['migration_multiple_version'] = 'There are multiple migrations with the same version number: %s.';\n$lang['migration_class_doesnt_exist'] = 'The migration class \"%s\" could not be found.';\n$lang['migration_missing_up_method'] = 'The migration class \"%s\" is missing an \"up\" method.';\n$lang['migration_missing_down_method'] = 'The migration class \"%s\" is missing a \"down\" method.';\n$lang['migration_invalid_filename'] = 'Migration \"%s\" has an invalid filename.';\n"
  },
  {
    "path": "system/language/english/number_lang.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n$lang['terabyte_abbr'] = 'TB';\n$lang['gigabyte_abbr'] = 'GB';\n$lang['megabyte_abbr'] = 'MB';\n$lang['kilobyte_abbr'] = 'KB';\n$lang['bytes'] = 'Bytes';\n"
  },
  {
    "path": "system/language/english/pagination_lang.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n$lang['pagination_first_link'] = '&lsaquo; First';\n$lang['pagination_next_link'] = '&gt;';\n$lang['pagination_prev_link'] = '&lt;';\n$lang['pagination_last_link'] = 'Last &rsaquo;';\n"
  },
  {
    "path": "system/language/english/profiler_lang.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n$lang['profiler_database'] = 'DATABASE';\n$lang['profiler_controller_info'] = 'CLASS/METHOD';\n$lang['profiler_benchmarks'] = 'BENCHMARKS';\n$lang['profiler_queries'] = 'QUERIES';\n$lang['profiler_get_data'] = 'GET DATA';\n$lang['profiler_post_data'] = 'POST DATA';\n$lang['profiler_uri_string'] = 'URI STRING';\n$lang['profiler_memory_usage'] = 'MEMORY USAGE';\n$lang['profiler_config'] = 'CONFIG VARIABLES';\n$lang['profiler_session_data'] = 'SESSION DATA';\n$lang['profiler_headers'] = 'HTTP HEADERS';\n$lang['profiler_no_db'] = 'Database driver is not currently loaded';\n$lang['profiler_no_queries'] = 'No queries were run';\n$lang['profiler_no_post'] = 'No POST data exists';\n$lang['profiler_no_get'] = 'No GET data exists';\n$lang['profiler_no_uri'] = 'No URI data exists';\n$lang['profiler_no_memory'] = 'Memory Usage Unavailable';\n$lang['profiler_no_profiles'] = 'No Profile data - all Profiler sections have been disabled.';\n$lang['profiler_section_hide'] = 'Hide';\n$lang['profiler_section_show'] = 'Show';\n$lang['profiler_seconds'] = 'seconds';\n"
  },
  {
    "path": "system/language/english/unit_test_lang.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n$lang['ut_test_name'] = 'Test Name';\n$lang['ut_test_datatype'] = 'Test Datatype';\n$lang['ut_res_datatype'] = 'Expected Datatype';\n$lang['ut_result'] = 'Result';\n$lang['ut_undefined'] = 'Undefined Test Name';\n$lang['ut_file'] = 'File Name';\n$lang['ut_line'] = 'Line Number';\n$lang['ut_passed'] = 'Passed';\n$lang['ut_failed'] = 'Failed';\n$lang['ut_boolean'] = 'Boolean';\n$lang['ut_integer'] = 'Integer';\n$lang['ut_float'] = 'Float';\n$lang['ut_double'] = 'Float'; // can be the same as float\n$lang['ut_string'] = 'String';\n$lang['ut_array'] = 'Array';\n$lang['ut_object'] = 'Object';\n$lang['ut_resource'] = 'Resource';\n$lang['ut_null'] = 'Null';\n$lang['ut_notes'] = 'Notes';\n"
  },
  {
    "path": "system/language/english/upload_lang.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n$lang['upload_userfile_not_set'] = 'Unable to find a post variable called userfile.';\n$lang['upload_file_exceeds_limit'] = 'The uploaded file exceeds the maximum allowed size in your PHP configuration file.';\n$lang['upload_file_exceeds_form_limit'] = 'The uploaded file exceeds the maximum size allowed by the submission form.';\n$lang['upload_file_partial'] = 'The file was only partially uploaded.';\n$lang['upload_no_temp_directory'] = 'The temporary folder is missing.';\n$lang['upload_unable_to_write_file'] = 'The file could not be written to disk.';\n$lang['upload_stopped_by_extension'] = 'The file upload was stopped by extension.';\n$lang['upload_no_file_selected'] = 'You did not select a file to upload.';\n$lang['upload_invalid_filetype'] = 'The filetype you are attempting to upload is not allowed.';\n$lang['upload_invalid_filesize'] = 'The file you are attempting to upload is larger than the permitted size.';\n$lang['upload_invalid_dimensions'] = 'The image you are attempting to upload doesn\\'t fit into the allowed dimensions.';\n$lang['upload_destination_error'] = 'A problem was encountered while attempting to move the uploaded file to the final destination.';\n$lang['upload_no_filepath'] = 'The upload path does not appear to be valid.';\n$lang['upload_no_file_types'] = 'You have not specified any allowed file types.';\n$lang['upload_bad_filename'] = 'The file name you submitted already exists on the server.';\n$lang['upload_not_writable'] = 'The upload destination folder does not appear to be writable.';\n"
  },
  {
    "path": "system/language/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/libraries/Cache/Cache.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 2.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Caching Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tCore\n * @author\t\tEllisLab Dev Team\n * @link\n */\nclass CI_Cache extends CI_Driver_Library {\n\n\t/**\n\t * Valid cache drivers\n\t *\n\t * @var array\n\t */\n\tprotected $valid_drivers = array(\n\t\t'apc',\n\t\t'apcu',\n\t\t'dummy',\n\t\t'file',\n\t\t'memcached',\n\t\t'redis',\n\t\t'wincache'\n\t);\n\n\t/**\n\t * Path of cache files (if file-based cache)\n\t *\n\t * @var string\n\t */\n\tprotected $_cache_path = NULL;\n\n\t/**\n\t * Reference to the driver\n\t *\n\t * @var mixed\n\t */\n\tprotected $_adapter = 'dummy';\n\n\t/**\n\t * Fallback driver\n\t *\n\t * @var string\n\t */\n\tprotected $_backup_driver = 'dummy';\n\n\t/**\n\t * Cache key prefix\n\t *\n\t * @var\tstring\n\t */\n\tpublic $key_prefix = '';\n\n\t/**\n\t * Constructor\n\t *\n\t * Initialize class properties based on the configuration array.\n\t *\n\t * @param\tarray\t$config = array()\n\t * @return\tvoid\n\t */\n\tpublic function __construct($config = array())\n\t{\n\t\tisset($config['adapter']) && $this->_adapter = $config['adapter'];\n\t\tisset($config['backup']) && $this->_backup_driver = $config['backup'];\n\t\tisset($config['key_prefix']) && $this->key_prefix = $config['key_prefix'];\n\n\t\t// If the specified adapter isn't available, check the backup.\n\t\tif ( ! $this->is_supported($this->_adapter))\n\t\t{\n\t\t\tif ( ! $this->is_supported($this->_backup_driver))\n\t\t\t{\n\t\t\t\t// Backup isn't supported either. Default to 'Dummy' driver.\n\t\t\t\tlog_message('error', 'Cache adapter \"'.$this->_adapter.'\" and backup \"'.$this->_backup_driver.'\" are both unavailable. Cache is now using \"Dummy\" adapter.');\n\t\t\t\t$this->_adapter = 'dummy';\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Backup is supported. Set it to primary.\n\t\t\t\tlog_message('debug', 'Cache adapter \"'.$this->_adapter.'\" is unavailable. Falling back to \"'.$this->_backup_driver.'\" backup adapter.');\n\t\t\t\t$this->_adapter = $this->_backup_driver;\n\t\t\t}\n\t\t}\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get\n\t *\n\t * Look for a value in the cache. If it exists, return the data\n\t * if not, return FALSE\n\t *\n\t * @param\tstring\t$id\n\t * @return\tmixed\tvalue matching $id or FALSE on failure\n\t */\n\tpublic function get($id)\n\t{\n\t\treturn $this->{$this->_adapter}->get($this->key_prefix.$id);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Cache Save\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @param\tmixed\t$data\tData to store\n\t * @param\tint\t$ttl\tCache TTL (in seconds)\n\t * @param\tbool\t$raw\tWhether to store the raw value\n\t * @return\tbool\tTRUE on success, FALSE on failure\n\t */\n\tpublic function save($id, $data, $ttl = 60, $raw = FALSE)\n\t{\n\t\treturn $this->{$this->_adapter}->save($this->key_prefix.$id, $data, $ttl, $raw);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Delete from Cache\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @return\tbool\tTRUE on success, FALSE on failure\n\t */\n\tpublic function delete($id)\n\t{\n\t\treturn $this->{$this->_adapter}->delete($this->key_prefix.$id);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Increment a raw value\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @param\tint\t$offset\tStep/value to add\n\t * @return\tmixed\tNew value on success or FALSE on failure\n\t */\n\tpublic function increment($id, $offset = 1)\n\t{\n\t\treturn $this->{$this->_adapter}->increment($this->key_prefix.$id, $offset);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Decrement a raw value\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @param\tint\t$offset\tStep/value to reduce by\n\t * @return\tmixed\tNew value on success or FALSE on failure\n\t */\n\tpublic function decrement($id, $offset = 1)\n\t{\n\t\treturn $this->{$this->_adapter}->decrement($this->key_prefix.$id, $offset);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Clean the cache\n\t *\n\t * @return\tbool\tTRUE on success, FALSE on failure\n\t */\n\tpublic function clean()\n\t{\n\t\treturn $this->{$this->_adapter}->clean();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Cache Info\n\t *\n\t * @param\tstring\t$type = 'user'\tuser/filehits\n\t * @return\tmixed\tarray containing cache info on success OR FALSE on failure\n\t */\n\tpublic function cache_info($type = 'user')\n\t{\n\t\treturn $this->{$this->_adapter}->cache_info($type);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get Cache Metadata\n\t *\n\t * @param\tstring\t$id\tkey to get cache metadata on\n\t * @return\tmixed\tcache item metadata\n\t */\n\tpublic function get_metadata($id)\n\t{\n\t\treturn $this->{$this->_adapter}->get_metadata($this->key_prefix.$id);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Is the requested driver supported in this environment?\n\t *\n\t * @param\tstring\t$driver\tThe driver to test\n\t * @return\tarray\n\t */\n\tpublic function is_supported($driver)\n\t{\n\t\tstatic $support;\n\n\t\tif ( ! isset($support, $support[$driver]))\n\t\t{\n\t\t\t$support[$driver] = $this->{$driver}->is_supported();\n\t\t}\n\n\t\treturn $support[$driver];\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get currently loaded driver\n\t *\n\t * @return string\n\t */\n\tpublic function get_loaded_driver()\n\t{\n\t\treturn $this->_adapter;\n\t}\n}\n"
  },
  {
    "path": "system/libraries/Cache/drivers/Cache_apc.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 2.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter APC Caching Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tCore\n * @author\t\tEllisLab Dev Team\n * @link\n */\nclass CI_Cache_apc extends CI_Driver {\n\n\t/**\n\t * Class constructor\n\t *\n\t * Only present so that an error message is logged\n\t * if APC is not available.\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __construct()\n\t{\n\t\tif ( ! $this->is_supported())\n\t\t{\n\t\t\tlog_message('error', 'Cache: Failed to initialize APC; extension not loaded/enabled?');\n\t\t}\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get\n\t *\n\t * Look for a value in the cache. If it exists, return the data\n\t * if not, return FALSE\n\t *\n\t * @param\tstring\n\t * @return\tmixed\tvalue that is stored/FALSE on failure\n\t */\n\tpublic function get($id)\n\t{\n\t\t$success = FALSE;\n\t\t$data = apc_fetch($id, $success);\n\n\t\treturn ($success === TRUE) ? $data : FALSE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Cache Save\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @param\tmixed\t$data\tData to store\n\t * @param\tint\t$ttl\tLength of time (in seconds) to cache the data\n\t * @param\tbool\t$raw\tWhether to store the raw value (unused)\n\t * @return\tbool\tTRUE on success, FALSE on failure\n\t */\n\tpublic function save($id, $data, $ttl = 60, $raw = FALSE)\n\t{\n\t\treturn apc_store($id, $data, (int) $ttl);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Delete from Cache\n\t *\n\t * @param\tmixed\tunique identifier of the item in the cache\n\t * @return\tbool\ttrue on success/false on failure\n\t */\n\tpublic function delete($id)\n\t{\n\t\treturn apc_delete($id);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Increment a raw value\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @param\tint\t$offset\tStep/value to add\n\t * @return\tmixed\tNew value on success or FALSE on failure\n\t */\n\tpublic function increment($id, $offset = 1)\n\t{\n\t\treturn apc_inc($id, $offset);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Decrement a raw value\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @param\tint\t$offset\tStep/value to reduce by\n\t * @return\tmixed\tNew value on success or FALSE on failure\n\t */\n\tpublic function decrement($id, $offset = 1)\n\t{\n\t\treturn apc_dec($id, $offset);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Clean the cache\n\t *\n\t * @return\tbool\tfalse on failure/true on success\n\t */\n\tpublic function clean()\n\t{\n\t\treturn apc_clear_cache('user');\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Cache Info\n\t *\n\t * @param\tstring\tuser/filehits\n\t * @return\tmixed\tarray on success, false on failure\n\t */\n\tpublic function cache_info($type = NULL)\n\t{\n\t\treturn apc_cache_info($type);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get Cache Metadata\n\t *\n\t * @param\tmixed\tkey to get cache metadata on\n\t * @return\tmixed\tarray on success/false on failure\n\t */\n\tpublic function get_metadata($id)\n\t{\n\t\t$cache_info = apc_cache_info('user', FALSE);\n\t\tif (empty($cache_info) OR empty($cache_info['cache_list']))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tforeach ($cache_info['cache_list'] as &$entry)\n\t\t{\n\t\t\tif ($entry['info'] !== $id)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$success  = FALSE;\n\t\t\t$metadata = array(\n\t\t\t\t'expire' => ($entry['ttl'] ? $entry['mtime'] + $entry['ttl'] : 0),\n\t\t\t\t'mtime'  => $entry['ttl'],\n\t\t\t\t'data'   => apc_fetch($id, $success)\n\t\t\t);\n\n\t\t\treturn ($success === TRUE) ? $metadata : FALSE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * is_supported()\n\t *\n\t * Check to see if APC is available on this system, bail if it isn't.\n\t *\n\t * @return\tbool\n\t */\n\tpublic function is_supported()\n\t{\n\t\treturn (extension_loaded('apc') && ini_get('apc.enabled'));\n\t}\n}\n"
  },
  {
    "path": "system/libraries/Cache/drivers/Cache_apcu.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2014 - 2019, British Columbia Institute of Technology\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.2.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter APCu Caching Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tCore\n * @author\t\tCodeIgniter Dev team\n */\nclass CI_Cache_apcu extends CI_Driver {\n\n\t/**\n\t * Class constructor\n\t *\n\t * Only present so that an error message is logged\n\t * if APCu is not available.\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __construct()\n\t{\n\t\tif ( ! $this->is_supported())\n\t\t{\n\t\t\tlog_message('error', 'Cache: Failed to initialize APCu; extension not loaded/enabled?');\n\t\t}\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get\n\t *\n\t * Look for a value in the cache. If it exists, return the data\n\t * if not, return FALSE\n\t *\n\t * @param\tstring\n\t * @return\tmixed\tvalue that is stored/FALSE on failure\n\t */\n\tpublic function get($id)\n\t{\n\t\t$success = FALSE;\n\t\t$data = apcu_fetch($id, $success);\n\n\t\tif ($success === TRUE)\n\t\t{\n\t\t\treturn is_array($data)\n\t\t\t\t? $data[0]\n\t\t\t\t: $data;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Cache Save\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @param\tmixed\t$data\tData to store\n\t * @param\tint\t$ttl\tLength of time (in seconds) to cache the data\n\t * @param\tbool\t$raw\tWhether to store the raw value\n\t * @return\tbool\tTRUE on success, FALSE on failure\n\t */\n\tpublic function save($id, $data, $ttl = 60, $raw = FALSE)\n\t{\n\t\t$ttl = (int) $ttl;\n\n\t\treturn apcu_store(\n\t\t\t$id,\n\t\t\t($raw === TRUE ? $data : array($data, time(), $ttl)),\n\t\t\t$ttl\n\t\t);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Delete from Cache\n\t *\n\t * @param\tmixed\tunique identifier of the item in the cache\n\t * @return\tbool\ttrue on success/false on failure\n\t */\n\tpublic function delete($id)\n\t{\n\t\treturn apcu_delete($id);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Increment a raw value\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @param\tint\t$offset\tStep/value to add\n\t * @return\tmixed\tNew value on success or FALSE on failure\n\t */\n\tpublic function increment($id, $offset = 1)\n\t{\n\t\treturn apcu_inc($id, $offset);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Decrement a raw value\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @param\tint\t$offset\tStep/value to reduce by\n\t * @return\tmixed\tNew value on success or FALSE on failure\n\t */\n\tpublic function decrement($id, $offset = 1)\n\t{\n\t\treturn apcu_dec($id, $offset);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Clean the cache\n\t *\n\t * @return\tbool\tfalse on failure/true on success\n\t */\n\tpublic function clean()\n\t{\n\t\treturn apcu_clear_cache();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Cache Info\n\t *\n\t * @return\tmixed\tarray on success, false on failure\n\t */\n\tpublic function cache_info()\n\t{\n\t\treturn apcu_cache_info();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get Cache Metadata\n\t *\n\t * @param\tmixed\tkey to get cache metadata on\n\t * @return\tmixed\tarray on success/false on failure\n\t */\n\tpublic function get_metadata($id)\n\t{\n\t\t$success = FALSE;\n\t\t$stored = apcu_fetch($id, $success);\n\n\t\tif ($success === FALSE OR count($stored) !== 3)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tlist($data, $time, $ttl) = $stored;\n\n\t\treturn array(\n\t\t\t'expire'  => $time + $ttl,\n\t\t\t'mtime'   => $time,\n\t\t\t'data'    => $data\n\t\t);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * is_supported()\n\t *\n\t * Check to see if APCu is available on this system, bail if it isn't.\n\t *\n\t * @return\tbool\n\t */\n\tpublic function is_supported()\n\t{\n\t\treturn (extension_loaded('apcu') && ini_get('apc.enabled'));\n\t}\n}"
  },
  {
    "path": "system/libraries/Cache/drivers/Cache_dummy.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 2.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Dummy Caching Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tCore\n * @author\t\tEllisLab Dev Team\n * @link\n */\nclass CI_Cache_dummy extends CI_Driver {\n\n\t/**\n\t * Get\n\t *\n\t * Since this is the dummy class, it's always going to return FALSE.\n\t *\n\t * @param\tstring\n\t * @return\tbool\tFALSE\n\t */\n\tpublic function get($id)\n\t{\n\t\treturn FALSE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Cache Save\n\t *\n\t * @param\tstring\tUnique Key\n\t * @param\tmixed\tData to store\n\t * @param\tint\tLength of time (in seconds) to cache the data\n\t * @param\tbool\tWhether to store the raw value\n\t * @return\tbool\tTRUE, Simulating success\n\t */\n\tpublic function save($id, $data, $ttl = 60, $raw = FALSE)\n\t{\n\t\treturn TRUE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Delete from Cache\n\t *\n\t * @param\tmixed\tunique identifier of the item in the cache\n\t * @return\tbool\tTRUE, simulating success\n\t */\n\tpublic function delete($id)\n\t{\n\t\treturn TRUE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Increment a raw value\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @param\tint\t$offset\tStep/value to add\n\t * @return\tmixed\tNew value on success or FALSE on failure\n\t */\n\tpublic function increment($id, $offset = 1)\n\t{\n\t\treturn TRUE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Decrement a raw value\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @param\tint\t$offset\tStep/value to reduce by\n\t * @return\tmixed\tNew value on success or FALSE on failure\n\t */\n\tpublic function decrement($id, $offset = 1)\n\t{\n\t\treturn TRUE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Clean the cache\n\t *\n\t * @return\tbool\tTRUE, simulating success\n\t */\n\tpublic function clean()\n\t{\n\t\treturn TRUE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Cache Info\n\t *\n\t * @param\tstring\tuser/filehits\n\t * @return\tbool\tFALSE\n\t */\n\tpublic function cache_info($type = NULL)\n\t{\n\t\treturn FALSE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get Cache Metadata\n\t *\n\t * @param\tmixed\tkey to get cache metadata on\n\t * @return\tbool\tFALSE\n\t */\n\tpublic function get_metadata($id)\n\t{\n\t\treturn FALSE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Is this caching driver supported on the system?\n\t * Of course this one is.\n\t *\n\t * @return\tbool\tTRUE\n\t */\n\tpublic function is_supported()\n\t{\n\t\treturn TRUE;\n\t}\n\n}\n"
  },
  {
    "path": "system/libraries/Cache/drivers/Cache_file.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 2.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter File Caching Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tCore\n * @author\t\tEllisLab Dev Team\n * @link\n */\nclass CI_Cache_file extends CI_Driver {\n\n\t/**\n\t * Directory in which to save cache files\n\t *\n\t * @var string\n\t */\n\tprotected $_cache_path;\n\n\t/**\n\t * Initialize file-based cache\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __construct()\n\t{\n\t\t$CI =& get_instance();\n\t\t$CI->load->helper('file');\n\t\t$path = $CI->config->item('cache_path');\n\t\t$this->_cache_path = ($path === '') ? APPPATH.'cache/' : $path;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Fetch from cache\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @return\tmixed\tData on success, FALSE on failure\n\t */\n\tpublic function get($id)\n\t{\n\t\t$data = $this->_get($id);\n\t\treturn is_array($data) ? $data['data'] : FALSE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Save into cache\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @param\tmixed\t$data\tData to store\n\t * @param\tint\t$ttl\tTime to live in seconds\n\t * @param\tbool\t$raw\tWhether to store the raw value (unused)\n\t * @return\tbool\tTRUE on success, FALSE on failure\n\t */\n\tpublic function save($id, $data, $ttl = 60, $raw = FALSE)\n\t{\n\t\t$contents = array(\n\t\t\t'time'\t\t=> time(),\n\t\t\t'ttl'\t\t=> $ttl,\n\t\t\t'data'\t\t=> $data\n\t\t);\n\n\t\tif (write_file($this->_cache_path.$id, serialize($contents)))\n\t\t{\n\t\t\tchmod($this->_cache_path.$id, 0640);\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Delete from Cache\n\t *\n\t * @param\tmixed\tunique identifier of item in cache\n\t * @return\tbool\ttrue on success/false on failure\n\t */\n\tpublic function delete($id)\n\t{\n\t\treturn is_file($this->_cache_path.$id) ? unlink($this->_cache_path.$id) : FALSE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Increment a raw value\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @param\tint\t$offset\tStep/value to add\n\t * @return\tNew value on success, FALSE on failure\n\t */\n\tpublic function increment($id, $offset = 1)\n\t{\n\t\t$data = $this->_get($id);\n\n\t\tif ($data === FALSE)\n\t\t{\n\t\t\t$data = array('data' => 0, 'ttl' => 60);\n\t\t}\n\t\telseif ( ! is_int($data['data']))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$new_value = $data['data'] + $offset;\n\t\treturn $this->save($id, $new_value, $data['ttl'])\n\t\t\t? $new_value\n\t\t\t: FALSE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Decrement a raw value\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @param\tint\t$offset\tStep/value to reduce by\n\t * @return\tNew value on success, FALSE on failure\n\t */\n\tpublic function decrement($id, $offset = 1)\n\t{\n\t\t$data = $this->_get($id);\n\n\t\tif ($data === FALSE)\n\t\t{\n\t\t\t$data = array('data' => 0, 'ttl' => 60);\n\t\t}\n\t\telseif ( ! is_int($data['data']))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$new_value = $data['data'] - $offset;\n\t\treturn $this->save($id, $new_value, $data['ttl'])\n\t\t\t? $new_value\n\t\t\t: FALSE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Clean the Cache\n\t *\n\t * @return\tbool\tfalse on failure/true on success\n\t */\n\tpublic function clean()\n\t{\n\t\treturn delete_files($this->_cache_path, FALSE, TRUE);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Cache Info\n\t *\n\t * Not supported by file-based caching\n\t *\n\t * @param\tstring\tuser/filehits\n\t * @return\tmixed\tFALSE\n\t */\n\tpublic function cache_info($type = NULL)\n\t{\n\t\treturn get_dir_file_info($this->_cache_path);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get Cache Metadata\n\t *\n\t * @param\tmixed\tkey to get cache metadata on\n\t * @return\tmixed\tFALSE on failure, array on success.\n\t */\n\tpublic function get_metadata($id)\n\t{\n\t\tif ( ! is_file($this->_cache_path.$id))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$data = unserialize(file_get_contents($this->_cache_path.$id));\n\n\t\tif (is_array($data))\n\t\t{\n\t\t\t$mtime = filemtime($this->_cache_path.$id);\n\n\t\t\tif ( ! isset($data['ttl'], $data['time']))\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\treturn array(\n\t\t\t\t'expire' => $data['time'] + $data['ttl'],\n\t\t\t\t'mtime'\t => $mtime\n\t\t\t);\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Is supported\n\t *\n\t * In the file driver, check to see that the cache directory is indeed writable\n\t *\n\t * @return\tbool\n\t */\n\tpublic function is_supported()\n\t{\n\t\treturn is_really_writable($this->_cache_path);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get all data\n\t *\n\t * Internal method to get all the relevant data about a cache item\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @return\tmixed\tData array on success or FALSE on failure\n\t */\n\tprotected function _get($id)\n\t{\n\t\tif ( ! is_file($this->_cache_path.$id))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$data = unserialize(file_get_contents($this->_cache_path.$id));\n\n\t\tif ($data['ttl'] > 0 && time() > $data['time'] + $data['ttl'])\n\t\t{\n\t\t\tfile_exists($this->_cache_path.$id) && unlink($this->_cache_path.$id);\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn $data;\n\t}\n\n}\n"
  },
  {
    "path": "system/libraries/Cache/drivers/Cache_memcached.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 2.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Memcached Caching Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tCore\n * @author\t\tEllisLab Dev Team\n * @link\n */\nclass CI_Cache_memcached extends CI_Driver {\n\n\t/**\n\t * Holds the memcached object\n\t *\n\t * @var object\n\t */\n\tprotected $_memcached;\n\n\t/**\n\t * Memcached configuration\n\t *\n\t * @var array\n\t */\n\tprotected $_config = array(\n\t\t'default' => array(\n\t\t\t'host'\t\t=> '127.0.0.1',\n\t\t\t'port'\t\t=> 11211,\n\t\t\t'weight'\t=> 1\n\t\t)\n\t);\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * Setup Memcache(d)\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __construct()\n\t{\n\t\t// Try to load memcached server info from the config file.\n\t\t$CI =& get_instance();\n\t\t$defaults = $this->_config['default'];\n\n\t\tif ($CI->config->load('memcached', TRUE, TRUE))\n\t\t{\n\t\t\t$this->_config = $CI->config->config['memcached'];\n\t\t}\n\n\t\tif (class_exists('Memcached', FALSE))\n\t\t{\n\t\t\t$this->_memcached = new Memcached();\n\t\t}\n\t\telseif (class_exists('Memcache', FALSE))\n\t\t{\n\t\t\t$this->_memcached = new Memcache();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlog_message('error', 'Cache: Failed to create Memcache(d) object; extension not loaded?');\n\t\t\treturn;\n\t\t}\n\n\t\tforeach ($this->_config as $cache_name => $cache_server)\n\t\t{\n\t\t\tif ( ! isset($cache_server['hostname']))\n\t\t\t{\n\t\t\t\tlog_message('debug', 'Cache: Memcache(d) configuration \"'.$cache_name.'\" doesn\\'t include a hostname; ignoring.');\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telseif ($cache_server['hostname'][0] === '/')\n\t\t\t{\n\t\t\t\t$cache_server['port'] = 0;\n\t\t\t}\n\t\t\telseif (empty($cache_server['port']))\n\t\t\t{\n\t\t\t\t$cache_server['port'] = $defaults['port'];\n\t\t\t}\n\n\t\t\tisset($cache_server['weight']) OR $cache_server['weight'] = $defaults['weight'];\n\n\t\t\tif ($this->_memcached instanceof Memcache)\n\t\t\t{\n\t\t\t\t// Third parameter is persistence and defaults to TRUE.\n\t\t\t\t$this->_memcached->addServer(\n\t\t\t\t\t$cache_server['hostname'],\n\t\t\t\t\t$cache_server['port'],\n\t\t\t\t\tTRUE,\n\t\t\t\t\t$cache_server['weight']\n\t\t\t\t);\n\t\t\t}\n\t\t\telseif ($this->_memcached instanceof Memcached)\n\t\t\t{\n\t\t\t\t$this->_memcached->addServer(\n\t\t\t\t\t$cache_server['hostname'],\n\t\t\t\t\t$cache_server['port'],\n\t\t\t\t\t$cache_server['weight']\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Fetch from cache\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @return\tmixed\tData on success, FALSE on failure\n\t */\n\tpublic function get($id)\n\t{\n\t\t$data = $this->_memcached->get($id);\n\n\t\treturn is_array($data) ? $data[0] : $data;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Save\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @param\tmixed\t$data\tData being cached\n\t * @param\tint\t$ttl\tTime to live\n\t * @param\tbool\t$raw\tWhether to store the raw value\n\t * @return\tbool\tTRUE on success, FALSE on failure\n\t */\n\tpublic function save($id, $data, $ttl = 60, $raw = FALSE)\n\t{\n\t\tif ($raw !== TRUE)\n\t\t{\n\t\t\t$data = array($data, time(), $ttl);\n\t\t}\n\n\t\tif ($this->_memcached instanceof Memcached)\n\t\t{\n\t\t\treturn $this->_memcached->set($id, $data, $ttl);\n\t\t}\n\t\telseif ($this->_memcached instanceof Memcache)\n\t\t{\n\t\t\treturn $this->_memcached->set($id, $data, 0, $ttl);\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Delete from Cache\n\t *\n\t * @param\tmixed\t$id\tkey to be deleted.\n\t * @return\tbool\ttrue on success, false on failure\n\t */\n\tpublic function delete($id)\n\t{\n\t\treturn $this->_memcached->delete($id);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Increment a raw value\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @param\tint\t$offset\tStep/value to add\n\t * @return\tmixed\tNew value on success or FALSE on failure\n\t */\n\tpublic function increment($id, $offset = 1)\n\t{\n\t\tif (($result = $this->_memcached->increment($id, $offset)) === FALSE)\n\t\t{\n\t\t\treturn $this->_memcached->add($id, $offset) ? $offset : FALSE;\n\t\t}\n\n\t\treturn $result;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Decrement a raw value\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @param\tint\t$offset\tStep/value to reduce by\n\t * @return\tmixed\tNew value on success or FALSE on failure\n\t */\n\tpublic function decrement($id, $offset = 1)\n\t{\n\t\tif (($result = $this->_memcached->decrement($id, $offset)) === FALSE)\n\t\t{\n\t\t\treturn $this->_memcached->add($id, 0) ? 0 : FALSE;\n\t\t}\n\n\t\treturn $result;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Clean the Cache\n\t *\n\t * @return\tbool\tfalse on failure/true on success\n\t */\n\tpublic function clean()\n\t{\n\t\treturn $this->_memcached->flush();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Cache Info\n\t *\n\t * @return\tmixed\tarray on success, false on failure\n\t */\n\tpublic function cache_info()\n\t{\n\t\treturn $this->_memcached->getStats();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get Cache Metadata\n\t *\n\t * @param\tmixed\t$id\tkey to get cache metadata on\n\t * @return\tmixed\tFALSE on failure, array on success.\n\t */\n\tpublic function get_metadata($id)\n\t{\n\t\t$stored = $this->_memcached->get($id);\n\n\t\tif (count($stored) !== 3)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tlist($data, $time, $ttl) = $stored;\n\n\t\treturn array(\n\t\t\t'expire'\t=> $time + $ttl,\n\t\t\t'mtime'\t\t=> $time,\n\t\t\t'data'\t\t=> $data\n\t\t);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Is supported\n\t *\n\t * Returns FALSE if memcached is not supported on the system.\n\t * If it is, we setup the memcached object & return TRUE\n\t *\n\t * @return\tbool\n\t */\n\tpublic function is_supported()\n\t{\n\t\treturn (extension_loaded('memcached') OR extension_loaded('memcache'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Class destructor\n\t *\n\t * Closes the connection to Memcache(d) if present.\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __destruct()\n\t{\n\t\tif ($this->_memcached instanceof Memcache)\n\t\t{\n\t\t\t$this->_memcached->close();\n\t\t}\n\t\telseif ($this->_memcached instanceof Memcached && method_exists($this->_memcached, 'quit'))\n\t\t{\n\t\t\t$this->_memcached->quit();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "system/libraries/Cache/drivers/Cache_redis.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Redis Caching Class\n *\n * @package\t   CodeIgniter\n * @subpackage Libraries\n * @category   Core\n * @author\t   Anton Lindqvist <anton@qvister.se>\n * @link\n */\nclass CI_Cache_redis extends CI_Driver\n{\n\t/**\n\t * Default config\n\t *\n\t * @static\n\t * @var\tarray\n\t */\n\tprotected static $_default_config = array(\n\t\t'host' => '127.0.0.1',\n\t\t'password' => NULL,\n\t\t'port' => 6379,\n\t\t'timeout' => 0.0,\n\t\t'database' => 0\n\t);\n\n\t/**\n\t * Redis connection\n\t *\n\t * @var\tRedis\n\t */\n\tprotected $_redis;\n\n\t/**\n\t * del()/delete() method name depending on phpRedis version\n\t *\n\t * @var\tstring\n\t */\n\tprotected static $_delete_name;\n\n\t/**\n\t * sRem()/sRemove() method name depending on phpRedis version\n\t *\n\t * @var\tstring\n\t */\n\tprotected static $_sRemove_name;\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * Setup Redis\n\t *\n\t * Loads Redis config file if present. Will halt execution\n\t * if a Redis connection can't be established.\n\t *\n\t * @return\tvoid\n\t * @throws\tRedisException\n\t * @see\t\tRedis::connect()\n\t */\n\tpublic function __construct()\n\t{\n\t\tif ( ! $this->is_supported())\n\t\t{\n\t\t\tlog_message('error', 'Cache: Failed to create Redis object; extension not loaded?');\n\t\t\treturn;\n\t\t}\n\n\t\tif ( ! isset(static::$_delete_name, static::$_sRemove_name))\n\t\t{\n\t\t\tif (version_compare(phpversion('redis'), '5', '>='))\n\t\t\t{\n\t\t\t\tstatic::$_delete_name  = 'del';\n\t\t\t\tstatic::$_sRemove_name = 'sRem';\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstatic::$_delete_name  = 'delete';\n\t\t\t\tstatic::$_sRemove_name = 'sRemove';\n\t\t\t}\n\t\t}\n\n\t\t$CI =& get_instance();\n\n\t\tif ($CI->config->load('redis', TRUE, TRUE))\n\t\t{\n\t\t\t$config = array_merge(self::$_default_config, $CI->config->item('redis'));\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$config = self::$_default_config;\n\t\t}\n\n\t\t$this->_redis = new Redis();\n\n\t\t// The following calls used to be wrapped in a try ... catch\n\t\t// and just log an error, but that only causes more errors later.\n\t\tif ( ! $this->_redis->connect($config['host'], ($config['host'][0] === '/' ? 0 : $config['port']), $config['timeout']))\n\t\t{\n\t\t\tlog_message('error', 'Cache: Redis connection failed. Check your configuration.');\n\t\t}\n\n\t\tif (isset($config['password']) && ! $this->_redis->auth($config['password']))\n\t\t{\n\t\t\tlog_message('error', 'Cache: Redis authentication failed.');\n\t\t}\n\n\t\tif (isset($config['database']) && $config['database'] > 0 && ! $this->_redis->select($config['database']))\n\t\t{\n\t\t\tlog_message('error', 'Cache: Redis select database failed.');\n\t\t}\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get cache\n\t *\n\t * @param\tstring\t$key\tCache ID\n\t * @return\tmixed\n\t */\n\tpublic function get($key)\n\t{\n\t\t$data = $this->_redis->hMGet($key, array('__ci_type', '__ci_value'));\n\n\t\tif ($value !== FALSE && $this->_redis->sIsMember('_ci_redis_serialized', $key))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tswitch ($data['__ci_type'])\n\t\t{\n\t\t\tcase 'array':\n\t\t\tcase 'object':\n\t\t\t\treturn unserialize($data['__ci_value']);\n\t\t\tcase 'boolean':\n\t\t\tcase 'integer':\n\t\t\tcase 'double': // Yes, 'double' is returned and NOT 'float'\n\t\t\tcase 'string':\n\t\t\tcase 'NULL':\n\t\t\t\treturn settype($data['__ci_value'], $data['__ci_type'])\n\t\t\t\t\t? $data['__ci_value']\n\t\t\t\t\t: FALSE;\n\t\t\tcase 'resource':\n\t\t\tdefault:\n\t\t\t\treturn FALSE;\n\t\t}\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Save cache\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @param\tmixed\t$data\tData to save\n\t * @param\tint\t$ttl\tTime to live in seconds\n\t * @param\tbool\t$raw\tWhether to store the raw value (unused)\n\t * @return\tbool\tTRUE on success, FALSE on failure\n\t */\n\tpublic function save($id, $data, $ttl = 60, $raw = FALSE)\n\t{\n\t\tswitch ($data_type = gettype($data))\n\t\t{\n\t\t\tcase 'array':\n\t\t\tcase 'object':\n\t\t\t\t$data = serialize($data);\n\t\t\t\tbreak;\n\t\t\tcase 'boolean':\n\t\t\tcase 'integer':\n\t\t\tcase 'double': // Yes, 'double' is returned and NOT 'float'\n\t\t\tcase 'string':\n\t\t\tcase 'NULL':\n\t\t\t\tbreak;\n\t\t\tcase 'resource':\n\t\t\tdefault:\n\t\t\t\treturn FALSE;\n\t\t}\n\n\t\tif ( ! $this->_redis->hMSet($id, array('__ci_type' => $data_type, '__ci_value' => $data)))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$this->_redis->{static::$_sRemove_name}('_ci_redis_serialized', $id);\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Delete from cache\n\t *\n\t * @param\tstring\t$key\tCache key\n\t * @return\tbool\n\t */\n\tpublic function delete($key)\n\t{\n\t\tif ($this->_redis->{static::$_delete_name}($key) !== 1)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$this->_redis->{static::$_sRemove_name}('_ci_redis_serialized', $key);\n\n\t\treturn TRUE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Increment a raw value\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @param\tint\t$offset\tStep/value to add\n\t * @return\tmixed\tNew value on success or FALSE on failure\n\t */\n\tpublic function increment($id, $offset = 1)\n\t{\n\t\treturn $this->_redis->incrBy($id, $offset);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Decrement a raw value\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @param\tint\t$offset\tStep/value to reduce by\n\t * @return\tmixed\tNew value on success or FALSE on failure\n\t */\n\tpublic function decrement($id, $offset = 1)\n\t{\n\t\treturn $this->_redis->decrBy($id, $offset);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Clean cache\n\t *\n\t * @return\tbool\n\t * @see\t\tRedis::flushDB()\n\t */\n\tpublic function clean()\n\t{\n\t\treturn $this->_redis->flushDB();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get cache driver info\n\t *\n\t * @param\tstring\t$type\tNot supported in Redis.\n\t *\t\t\t\tOnly included in order to offer a\n\t *\t\t\t\tconsistent cache API.\n\t * @return\tarray\n\t * @see\t\tRedis::info()\n\t */\n\tpublic function cache_info($type = NULL)\n\t{\n\t\treturn $this->_redis->info();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get cache metadata\n\t *\n\t * @param\tstring\t$key\tCache key\n\t * @return\tarray\n\t */\n\tpublic function get_metadata($key)\n\t{\n\t\t$value = $this->get($key);\n\n\t\tif ($value !== FALSE)\n\t\t{\n\t\t\treturn array(\n\t\t\t\t'expire' => time() + $this->_redis->ttl($key),\n\t\t\t\t'data' => $value\n\t\t\t);\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Check if Redis driver is supported\n\t *\n\t * @return\tbool\n\t */\n\tpublic function is_supported()\n\t{\n\t\treturn extension_loaded('redis');\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Class destructor\n\t *\n\t * Closes the connection to Redis if present.\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __destruct()\n\t{\n\t\tif ($this->_redis)\n\t\t{\n\t\t\t$this->_redis->close();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "system/libraries/Cache/drivers/Cache_wincache.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Wincache Caching Class\n *\n * Read more about Wincache functions here:\n * https://www.php.net/manual/en/ref.wincache.php\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tCore\n * @author\t\tMike Murkovic\n * @link\n */\nclass CI_Cache_wincache extends CI_Driver {\n\n\t/**\n\t * Class constructor\n\t *\n\t * Only present so that an error message is logged\n\t * if APC is not available.\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __construct()\n\t{\n\t\tif ( ! $this->is_supported())\n\t\t{\n\t\t\tlog_message('error', 'Cache: Failed to initialize Wincache; extension not loaded/enabled?');\n\t\t}\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get\n\t *\n\t * Look for a value in the cache. If it exists, return the data,\n\t * if not, return FALSE\n\t *\n\t * @param\tstring\t$id\tCache Ide\n\t * @return\tmixed\tValue that is stored/FALSE on failure\n\t */\n\tpublic function get($id)\n\t{\n\t\t$success = FALSE;\n\t\t$data = wincache_ucache_get($id, $success);\n\n\t\t// Success returned by reference from wincache_ucache_get()\n\t\treturn ($success) ? $data : FALSE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Cache Save\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @param\tmixed\t$data\tData to store\n\t * @param\tint\t$ttl\tTime to live (in seconds)\n\t * @param\tbool\t$raw\tWhether to store the raw value (unused)\n\t * @return\tbool\ttrue on success/false on failure\n\t */\n\tpublic function save($id, $data, $ttl = 60, $raw = FALSE)\n\t{\n\t\treturn wincache_ucache_set($id, $data, $ttl);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Delete from Cache\n\t *\n\t * @param\tmixed\tunique identifier of the item in the cache\n\t * @return\tbool\ttrue on success/false on failure\n\t */\n\tpublic function delete($id)\n\t{\n\t\treturn wincache_ucache_delete($id);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Increment a raw value\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @param\tint\t$offset\tStep/value to add\n\t * @return\tmixed\tNew value on success or FALSE on failure\n\t */\n\tpublic function increment($id, $offset = 1)\n\t{\n\t\t$success = FALSE;\n\t\t$value = wincache_ucache_inc($id, $offset, $success);\n\n\t\treturn ($success === TRUE) ? $value : FALSE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Decrement a raw value\n\t *\n\t * @param\tstring\t$id\tCache ID\n\t * @param\tint\t$offset\tStep/value to reduce by\n\t * @return\tmixed\tNew value on success or FALSE on failure\n\t */\n\tpublic function decrement($id, $offset = 1)\n\t{\n\t\t$success = FALSE;\n\t\t$value = wincache_ucache_dec($id, $offset, $success);\n\n\t\treturn ($success === TRUE) ? $value : FALSE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Clean the cache\n\t *\n\t * @return\tbool\tfalse on failure/true on success\n\t */\n\tpublic function clean()\n\t{\n\t\treturn wincache_ucache_clear();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Cache Info\n\t *\n\t * @return\tmixed\tarray on success, false on failure\n\t */\n\tpublic function cache_info()\n\t{\n\t\treturn wincache_ucache_info(TRUE);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get Cache Metadata\n\t *\n\t * @param\tmixed\tkey to get cache metadata on\n\t * @return\tmixed\tarray on success/false on failure\n\t */\n\tpublic function get_metadata($id)\n\t{\n\t\tif ($stored = wincache_ucache_info(FALSE, $id))\n\t\t{\n\t\t\t$age = $stored['ucache_entries'][1]['age_seconds'];\n\t\t\t$ttl = $stored['ucache_entries'][1]['ttl_seconds'];\n\t\t\t$hitcount = $stored['ucache_entries'][1]['hitcount'];\n\n\t\t\treturn array(\n\t\t\t\t'expire'\t=> $ttl - $age,\n\t\t\t\t'hitcount'\t=> $hitcount,\n\t\t\t\t'age'\t\t=> $age,\n\t\t\t\t'ttl'\t\t=> $ttl\n\t\t\t);\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * is_supported()\n\t *\n\t * Check to see if WinCache is available on this system, bail if it isn't.\n\t *\n\t * @return\tbool\n\t */\n\tpublic function is_supported()\n\t{\n\t\treturn (extension_loaded('wincache') && ini_get('wincache.ucenabled'));\n\t}\n}\n"
  },
  {
    "path": "system/libraries/Cache/drivers/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/libraries/Cache/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/libraries/Calendar.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Calendar Class\n *\n * This class enables the creation of calendars\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tLibraries\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/calendar.html\n */\nclass CI_Calendar {\n\n\t/**\n\t * Calendar layout template\n\t *\n\t * @var mixed\n\t */\n\tpublic $template = '';\n\n\t/**\n\t * Replacements array for template\n\t *\n\t * @var array\n\t */\n\tpublic $replacements = array();\n\n\t/**\n\t * Day of the week to start the calendar on\n\t *\n\t * @var string\n\t */\n\tpublic $start_day = 'sunday';\n\n\t/**\n\t * How to display months\n\t *\n\t * @var string\n\t */\n\tpublic $month_type = 'long';\n\n\t/**\n\t * How to display names of days\n\t *\n\t * @var string\n\t */\n\tpublic $day_type = 'abr';\n\n\t/**\n\t * Whether to show next/prev month links\n\t *\n\t * @var bool\n\t */\n\tpublic $show_next_prev = FALSE;\n\n\t/**\n\t * Url base to use for next/prev month links\n\t *\n\t * @var bool\n\t */\n\tpublic $next_prev_url = '';\n\n\t/**\n\t * Show days of other months\n\t *\n\t * @var bool\n\t */\n\tpublic $show_other_days = FALSE;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * CI Singleton\n\t *\n\t * @var object\n\t */\n\tprotected $CI;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * Loads the calendar language file and sets the default time reference.\n\t *\n\t * @uses\tCI_Lang::$is_loaded\n\t *\n\t * @param\tarray\t$config\tCalendar options\n\t * @return\tvoid\n\t */\n\tpublic function __construct($config = array())\n\t{\n\t\t$this->CI =& get_instance();\n\t\t$this->CI->lang->load('calendar');\n\n\t\tempty($config) OR $this->initialize($config);\n\n\t\tlog_message('info', 'Calendar Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Initialize the user preferences\n\t *\n\t * Accepts an associative array as input, containing display preferences\n\t *\n\t * @param\tarray\tconfig preferences\n\t * @return\tCI_Calendar\n\t */\n\tpublic function initialize($config = array())\n\t{\n\t\tforeach ($config as $key => $val)\n\t\t{\n\t\t\tif (isset($this->$key))\n\t\t\t{\n\t\t\t\t$this->$key = $val;\n\t\t\t}\n\t\t}\n\n\t\t// Set the next_prev_url to the controller if required but not defined\n\t\tif ($this->show_next_prev === TRUE && empty($this->next_prev_url))\n\t\t{\n\t\t\t$this->next_prev_url = $this->CI->config->site_url($this->CI->router->class.'/'.$this->CI->router->method);\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Generate the calendar\n\t *\n\t * @param\tint\tthe year\n\t * @param\tint\tthe month\n\t * @param\tarray\tthe data to be shown in the calendar cells\n\t * @return\tstring\n\t */\n\tpublic function generate($year = '', $month = '', $data = array())\n\t{\n\t\t$local_time = time();\n\n\t\t// Set and validate the supplied month/year\n\t\tif (empty($year))\n\t\t{\n\t\t\t$year = date('Y', $local_time);\n\t\t}\n\t\telseif (strlen($year) === 1)\n\t\t{\n\t\t\t$year = '200'.$year;\n\t\t}\n\t\telseif (strlen($year) === 2)\n\t\t{\n\t\t\t$year = '20'.$year;\n\t\t}\n\n\t\tif (empty($month))\n\t\t{\n\t\t\t$month = date('m', $local_time);\n\t\t}\n\t\telseif (strlen($month) === 1)\n\t\t{\n\t\t\t$month = '0'.$month;\n\t\t}\n\n\t\t$adjusted_date = $this->adjust_date($month, $year);\n\n\t\t$month\t= $adjusted_date['month'];\n\t\t$year\t= $adjusted_date['year'];\n\n\t\t// Determine the total days in the month\n\t\t$total_days = $this->get_total_days($month, $year);\n\n\t\t// Set the starting day of the week\n\t\t$start_days\t= array('sunday' => 0, 'monday' => 1, 'tuesday' => 2, 'wednesday' => 3, 'thursday' => 4, 'friday' => 5, 'saturday' => 6);\n\t\t$start_day\t= isset($start_days[$this->start_day]) ? $start_days[$this->start_day] : 0;\n\n\t\t// Set the starting day number\n\t\t$local_date = mktime(12, 0, 0, $month, 1, $year);\n\t\t$date = getdate($local_date);\n\t\t$day  = $start_day + 1 - $date['wday'];\n\n\t\twhile ($day > 1)\n\t\t{\n\t\t\t$day -= 7;\n\t\t}\n\n\t\t// Set the current month/year/day\n\t\t// We use this to determine the \"today\" date\n\t\t$cur_year\t= date('Y', $local_time);\n\t\t$cur_month\t= date('m', $local_time);\n\t\t$cur_day\t= date('j', $local_time);\n\n\t\t$is_current_month = ($cur_year == $year && $cur_month == $month);\n\n\t\t// Generate the template data array\n\t\t$this->parse_template();\n\n\t\t// Begin building the calendar output\n\t\t$out = $this->replacements['table_open'].\"\\n\\n\".$this->replacements['heading_row_start'].\"\\n\";\n\n\t\t// \"previous\" month link\n\t\tif ($this->show_next_prev === TRUE)\n\t\t{\n\t\t\t// Add a trailing slash to the URL if needed\n\t\t\t$this->next_prev_url = preg_replace('/(.+?)\\/*$/', '\\\\1/', $this->next_prev_url);\n\n\t\t\t$adjusted_date = $this->adjust_date($month - 1, $year);\n\t\t\t$out .= str_replace('{previous_url}', $this->next_prev_url.$adjusted_date['year'].'/'.$adjusted_date['month'], $this->replacements['heading_previous_cell']).\"\\n\";\n\t\t}\n\n\t\t// Heading containing the month/year\n\t\t$colspan = ($this->show_next_prev === TRUE) ? 5 : 7;\n\n\t\t$this->replacements['heading_title_cell'] = str_replace('{colspan}', $colspan,\n\t\t\t\t\t\t\t\tstr_replace('{heading}', $this->get_month_name($month).'&nbsp;'.$year, $this->replacements['heading_title_cell']));\n\n\t\t$out .= $this->replacements['heading_title_cell'].\"\\n\";\n\n\t\t// \"next\" month link\n\t\tif ($this->show_next_prev === TRUE)\n\t\t{\n\t\t\t$adjusted_date = $this->adjust_date($month + 1, $year);\n\t\t\t$out .= str_replace('{next_url}', $this->next_prev_url.$adjusted_date['year'].'/'.$adjusted_date['month'], $this->replacements['heading_next_cell']);\n\t\t}\n\n\t\t$out .= \"\\n\".$this->replacements['heading_row_end'].\"\\n\\n\"\n\t\t\t// Write the cells containing the days of the week\n\t\t\t.$this->replacements['week_row_start'].\"\\n\";\n\n\t\t$day_names = $this->get_day_names();\n\n\t\tfor ($i = 0; $i < 7; $i ++)\n\t\t{\n\t\t\t$out .= str_replace('{week_day}', $day_names[($start_day + $i) %7], $this->replacements['week_day_cell']);\n\t\t}\n\n\t\t$out .= \"\\n\".$this->replacements['week_row_end'].\"\\n\";\n\n\t\t// Build the main body of the calendar\n\t\twhile ($day <= $total_days)\n\t\t{\n\t\t\t$out .= \"\\n\".$this->replacements['cal_row_start'].\"\\n\";\n\n\t\t\tfor ($i = 0; $i < 7; $i++)\n\t\t\t{\n\t\t\t\tif ($day > 0 && $day <= $total_days)\n\t\t\t\t{\n\t\t\t\t\t$out .= ($is_current_month === TRUE && $day == $cur_day) ? $this->replacements['cal_cell_start_today'] : $this->replacements['cal_cell_start'];\n\n\t\t\t\t\tif (isset($data[$day]))\n\t\t\t\t\t{\n\t\t\t\t\t\t// Cells with content\n\t\t\t\t\t\t$temp = ($is_current_month === TRUE && $day == $cur_day) ?\n\t\t\t\t\t\t\t\t$this->replacements['cal_cell_content_today'] : $this->replacements['cal_cell_content'];\n\t\t\t\t\t\t$out .= str_replace(array('{content}', '{day}'), array($data[$day], $day), $temp);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// Cells with no content\n\t\t\t\t\t\t$temp = ($is_current_month === TRUE && $day == $cur_day) ?\n\t\t\t\t\t\t\t\t$this->replacements['cal_cell_no_content_today'] : $this->replacements['cal_cell_no_content'];\n\t\t\t\t\t\t$out .= str_replace('{day}', $day, $temp);\n\t\t\t\t\t}\n\n\t\t\t\t\t$out .= ($is_current_month === TRUE && $day == $cur_day) ? $this->replacements['cal_cell_end_today'] : $this->replacements['cal_cell_end'];\n\t\t\t\t}\n\t\t\t\telseif ($this->show_other_days === TRUE)\n\t\t\t\t{\n\t\t\t\t\t$out .= $this->replacements['cal_cell_start_other'];\n\n\t\t\t\t\tif ($day <= 0)\n\t\t\t\t\t{\n\t\t\t\t\t\t// Day of previous month\n\t\t\t\t\t\t$prev_month = $this->adjust_date($month - 1, $year);\n\t\t\t\t\t\t$prev_month_days = $this->get_total_days($prev_month['month'], $prev_month['year']);\n\t\t\t\t\t\t$out .= str_replace('{day}', $prev_month_days + $day, $this->replacements['cal_cell_other']);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// Day of next month\n\t\t\t\t\t\t$out .= str_replace('{day}', $day - $total_days, $this->replacements['cal_cell_other']);\n\t\t\t\t\t}\n\n\t\t\t\t\t$out .= $this->replacements['cal_cell_end_other'];\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// Blank cells\n\t\t\t\t\t$out .= $this->replacements['cal_cell_start'].$this->replacements['cal_cell_blank'].$this->replacements['cal_cell_end'];\n\t\t\t\t}\n\n\t\t\t\t$day++;\n\t\t\t}\n\n\t\t\t$out .= \"\\n\".$this->replacements['cal_row_end'].\"\\n\";\n\t\t}\n\n\t\treturn $out .= \"\\n\".$this->replacements['table_close'];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get Month Name\n\t *\n\t * Generates a textual month name based on the numeric\n\t * month provided.\n\t *\n\t * @param\tint\tthe month\n\t * @return\tstring\n\t */\n\tpublic function get_month_name($month)\n\t{\n\t\tif ($this->month_type === 'short')\n\t\t{\n\t\t\t$month_names = array('01' => 'cal_jan', '02' => 'cal_feb', '03' => 'cal_mar', '04' => 'cal_apr', '05' => 'cal_may', '06' => 'cal_jun', '07' => 'cal_jul', '08' => 'cal_aug', '09' => 'cal_sep', '10' => 'cal_oct', '11' => 'cal_nov', '12' => 'cal_dec');\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$month_names = array('01' => 'cal_january', '02' => 'cal_february', '03' => 'cal_march', '04' => 'cal_april', '05' => 'cal_mayl', '06' => 'cal_june', '07' => 'cal_july', '08' => 'cal_august', '09' => 'cal_september', '10' => 'cal_october', '11' => 'cal_november', '12' => 'cal_december');\n\t\t}\n\n\t\treturn ($this->CI->lang->line($month_names[$month]) === FALSE)\n\t\t\t? ucfirst(substr($month_names[$month], 4))\n\t\t\t: $this->CI->lang->line($month_names[$month]);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get Day Names\n\t *\n\t * Returns an array of day names (Sunday, Monday, etc.) based\n\t * on the type. Options: long, short, abr\n\t *\n\t * @param\tstring\n\t * @return\tarray\n\t */\n\tpublic function get_day_names($day_type = '')\n\t{\n\t\tif ($day_type !== '')\n\t\t{\n\t\t\t$this->day_type = $day_type;\n\t\t}\n\n\t\tif ($this->day_type === 'long')\n\t\t{\n\t\t\t$day_names = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');\n\t\t}\n\t\telseif ($this->day_type === 'short')\n\t\t{\n\t\t\t$day_names = array('sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat');\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$day_names = array('su', 'mo', 'tu', 'we', 'th', 'fr', 'sa');\n\t\t}\n\n\t\t$days = array();\n\t\tfor ($i = 0, $c = count($day_names); $i < $c; $i++)\n\t\t{\n\t\t\t$days[] = ($this->CI->lang->line('cal_'.$day_names[$i]) === FALSE) ? ucfirst($day_names[$i]) : $this->CI->lang->line('cal_'.$day_names[$i]);\n\t\t}\n\n\t\treturn $days;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Adjust Date\n\t *\n\t * This function makes sure that we have a valid month/year.\n\t * For example, if you submit 13 as the month, the year will\n\t * increment and the month will become January.\n\t *\n\t * @param\tint\tthe month\n\t * @param\tint\tthe year\n\t * @return\tarray\n\t */\n\tpublic function adjust_date($month, $year)\n\t{\n\t\t$date = array();\n\n\t\t$date['month']\t= $month;\n\t\t$date['year']\t= $year;\n\n\t\twhile ($date['month'] > 12)\n\t\t{\n\t\t\t$date['month'] -= 12;\n\t\t\t$date['year']++;\n\t\t}\n\n\t\twhile ($date['month'] <= 0)\n\t\t{\n\t\t\t$date['month'] += 12;\n\t\t\t$date['year']--;\n\t\t}\n\n\t\tif (strlen($date['month']) === 1)\n\t\t{\n\t\t\t$date['month'] = '0'.$date['month'];\n\t\t}\n\n\t\treturn $date;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Total days in a given month\n\t *\n\t * @param\tint\tthe month\n\t * @param\tint\tthe year\n\t * @return\tint\n\t */\n\tpublic function get_total_days($month, $year)\n\t{\n\t\t$this->CI->load->helper('date');\n\t\treturn days_in_month($month, $year);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Default Template Data\n\t *\n\t * This is used in the event that the user has not created their own template\n\t *\n\t * @return\tarray\n\t */\n\tpublic function default_template()\n\t{\n\t\treturn array(\n\t\t\t'table_open'\t\t\t\t=> '<table border=\"0\" cellpadding=\"4\" cellspacing=\"0\">',\n\t\t\t'heading_row_start'\t\t\t=> '<tr>',\n\t\t\t'heading_previous_cell'\t\t=> '<th><a href=\"{previous_url}\">&lt;&lt;</a></th>',\n\t\t\t'heading_title_cell'\t\t=> '<th colspan=\"{colspan}\">{heading}</th>',\n\t\t\t'heading_next_cell'\t\t\t=> '<th><a href=\"{next_url}\">&gt;&gt;</a></th>',\n\t\t\t'heading_row_end'\t\t\t=> '</tr>',\n\t\t\t'week_row_start'\t\t\t=> '<tr>',\n\t\t\t'week_day_cell'\t\t\t\t=> '<td>{week_day}</td>',\n\t\t\t'week_row_end'\t\t\t\t=> '</tr>',\n\t\t\t'cal_row_start'\t\t\t\t=> '<tr>',\n\t\t\t'cal_cell_start'\t\t\t=> '<td>',\n\t\t\t'cal_cell_start_today'\t\t=> '<td>',\n\t\t\t'cal_cell_start_other'\t\t=> '<td style=\"color: #666;\">',\n\t\t\t'cal_cell_content'\t\t\t=> '<a href=\"{content}\">{day}</a>',\n\t\t\t'cal_cell_content_today'\t=> '<a href=\"{content}\"><strong>{day}</strong></a>',\n\t\t\t'cal_cell_no_content'\t\t=> '{day}',\n\t\t\t'cal_cell_no_content_today'\t=> '<strong>{day}</strong>',\n\t\t\t'cal_cell_blank'\t\t\t=> '&nbsp;',\n\t\t\t'cal_cell_other'\t\t\t=> '{day}',\n\t\t\t'cal_cell_end'\t\t\t\t=> '</td>',\n\t\t\t'cal_cell_end_today'\t\t=> '</td>',\n\t\t\t'cal_cell_end_other'\t\t=> '</td>',\n\t\t\t'cal_row_end'\t\t\t\t=> '</tr>',\n\t\t\t'table_close'\t\t\t\t=> '</table>'\n\t\t);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Parse Template\n\t *\n\t * Harvests the data within the template {pseudo-variables}\n\t * used to display the calendar\n\t *\n\t * @return\tCI_Calendar\n\t */\n\tpublic function parse_template()\n\t{\n\t\t$this->replacements = $this->default_template();\n\n\t\tif (empty($this->template))\n\t\t{\n\t\t\treturn $this;\n\t\t}\n\n\t\tif (is_string($this->template))\n\t\t{\n\t\t\t$today = array('cal_cell_start_today', 'cal_cell_content_today', 'cal_cell_no_content_today', 'cal_cell_end_today');\n\n\t\t\tforeach (array('table_open', 'table_close', 'heading_row_start', 'heading_previous_cell', 'heading_title_cell', 'heading_next_cell', 'heading_row_end', 'week_row_start', 'week_day_cell', 'week_row_end', 'cal_row_start', 'cal_cell_start', 'cal_cell_content', 'cal_cell_no_content', 'cal_cell_blank', 'cal_cell_end', 'cal_row_end', 'cal_cell_start_today', 'cal_cell_content_today', 'cal_cell_no_content_today', 'cal_cell_end_today', 'cal_cell_start_other', 'cal_cell_other', 'cal_cell_end_other') as $val)\n\t\t\t{\n\t\t\t\tif (preg_match('/\\{'.$val.'\\}(.*?)\\{\\/'.$val.'\\}/si', $this->template, $match))\n\t\t\t\t{\n\t\t\t\t\t$this->replacements[$val] = $match[1];\n\t\t\t\t}\n\t\t\t\telseif (in_array($val, $today, TRUE))\n\t\t\t\t{\n\t\t\t\t\t$this->replacements[$val] = $this->replacements[substr($val, 0, -6)];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telseif (is_array($this->template))\n\t\t{\n\t\t\t$this->replacements = array_merge($this->replacements, $this->template);\n\t\t}\n\n\t\treturn $this;\n\t}\n\n}\n"
  },
  {
    "path": "system/libraries/Driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Driver Library Class\n *\n * This class enables you to create \"Driver\" libraries that add runtime ability\n * to extend the capabilities of a class via additional driver objects\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tLibraries\n * @author\t\tEllisLab Dev Team\n * @link\n */\nclass CI_Driver_Library {\n\n\t/**\n\t * Array of drivers that are available to use with the driver class\n\t *\n\t * @var array\n\t */\n\tprotected $valid_drivers = array();\n\n\t/**\n\t * Name of the current class - usually the driver class\n\t *\n\t * @var string\n\t */\n\tprotected $lib_name;\n\n\t/**\n\t * Get magic method\n\t *\n\t * The first time a child is used it won't exist, so we instantiate it\n\t * subsequents calls will go straight to the proper child.\n\t *\n\t * @param\tstring\tChild class name\n\t * @return\tobject\tChild class\n\t */\n\tpublic function __get($child)\n\t{\n\t\t// Try to load the driver\n\t\treturn $this->load_driver($child);\n\t}\n\n\t/**\n\t * Load driver\n\t *\n\t * Separate load_driver call to support explicit driver load by library or user\n\t *\n\t * @param\tstring\tDriver name (w/o parent prefix)\n\t * @return\tobject\tChild class\n\t */\n\tpublic function load_driver($child)\n\t{\n\t\t// Get CodeIgniter instance and subclass prefix\n\t\t$prefix = config_item('subclass_prefix');\n\n\t\tif ( ! isset($this->lib_name))\n\t\t{\n\t\t\t// Get library name without any prefix\n\t\t\t$this->lib_name = str_replace(array('CI_', $prefix), '', get_class($this));\n\t\t}\n\n\t\t// The child will be prefixed with the parent lib\n\t\t$child_name = $this->lib_name.'_'.$child;\n\n\t\t// See if requested child is a valid driver\n\t\tif ( ! in_array($child, $this->valid_drivers))\n\t\t{\n\t\t\t// The requested driver isn't valid!\n\t\t\t$msg = 'Invalid driver requested: '.$child_name;\n\t\t\tlog_message('error', $msg);\n\t\t\tshow_error($msg);\n\t\t}\n\n\t\t// Get package paths and filename case variations to search\n\t\t$CI = get_instance();\n\t\t$paths = $CI->load->get_package_paths(TRUE);\n\n\t\t// Is there an extension?\n\t\t$class_name = $prefix.$child_name;\n\t\t$found = class_exists($class_name, FALSE);\n\t\tif ( ! $found)\n\t\t{\n\t\t\t// Check for subclass file\n\t\t\tforeach ($paths as $path)\n\t\t\t{\n\t\t\t\t// Does the file exist?\n\t\t\t\t$file = $path.'libraries/'.$this->lib_name.'/drivers/'.$prefix.$child_name.'.php';\n\t\t\t\tif (file_exists($file))\n\t\t\t\t{\n\t\t\t\t\t// Yes - require base class from BASEPATH\n\t\t\t\t\t$basepath = BASEPATH.'libraries/'.$this->lib_name.'/drivers/'.$child_name.'.php';\n\t\t\t\t\tif ( ! file_exists($basepath))\n\t\t\t\t\t{\n\t\t\t\t\t\t$msg = 'Unable to load the requested class: CI_'.$child_name;\n\t\t\t\t\t\tlog_message('error', $msg);\n\t\t\t\t\t\tshow_error($msg);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Include both sources and mark found\n\t\t\t\t\tinclude_once($basepath);\n\t\t\t\t\tinclude_once($file);\n\t\t\t\t\t$found = TRUE;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Do we need to search for the class?\n\t\tif ( ! $found)\n\t\t{\n\t\t\t// Use standard class name\n\t\t\t$class_name = 'CI_'.$child_name;\n\t\t\tif ( ! class_exists($class_name, FALSE))\n\t\t\t{\n\t\t\t\t// Check package paths\n\t\t\t\tforeach ($paths as $path)\n\t\t\t\t{\n\t\t\t\t\t// Does the file exist?\n\t\t\t\t\t$file = $path.'libraries/'.$this->lib_name.'/drivers/'.$child_name.'.php';\n\t\t\t\t\tif (file_exists($file))\n\t\t\t\t\t{\n\t\t\t\t\t\t// Include source\n\t\t\t\t\t\tinclude_once($file);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Did we finally find the class?\n\t\tif ( ! class_exists($class_name, FALSE))\n\t\t{\n\t\t\tif (class_exists($child_name, FALSE))\n\t\t\t{\n\t\t\t\t$class_name = $child_name;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$msg = 'Unable to load the requested driver: '.$class_name;\n\t\t\t\tlog_message('error', $msg);\n\t\t\t\tshow_error($msg);\n\t\t\t}\n\t\t}\n\n\t\t// Instantiate, decorate and add child\n\t\t$obj = new $class_name();\n\t\t$obj->decorate($this);\n\t\t$this->$child = $obj;\n\t\treturn $this->$child;\n\t}\n\n}\n\n// --------------------------------------------------------------------------\n\n/**\n * CodeIgniter Driver Class\n *\n * This class enables you to create drivers for a Library based on the Driver Library.\n * It handles the drivers' access to the parent library\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tLibraries\n * @author\t\tEllisLab Dev Team\n * @link\n */\nclass CI_Driver {\n\n\t/**\n\t * Instance of the parent class\n\t *\n\t * @var object\n\t */\n\tprotected $_parent;\n\n\t/**\n\t * List of methods in the parent class\n\t *\n\t * @var array\n\t */\n\tprotected $_methods = array();\n\n\t/**\n\t * List of properties in the parent class\n\t *\n\t * @var array\n\t */\n\tprotected $_properties = array();\n\n\t/**\n\t * Array of methods and properties for the parent class(es)\n\t *\n\t * @static\n\t * @var\tarray\n\t */\n\tprotected static $_reflections = array();\n\n\t/**\n\t * Decorate\n\t *\n\t * Decorates the child with the parent driver lib's methods and properties\n\t *\n\t * @param\tobject\n\t * @return\tvoid\n\t */\n\tpublic function decorate($parent)\n\t{\n\t\t$this->_parent = $parent;\n\n\t\t// Lock down attributes to what is defined in the class\n\t\t// and speed up references in magic methods\n\n\t\t$class_name = get_class($parent);\n\n\t\tif ( ! isset(self::$_reflections[$class_name]))\n\t\t{\n\t\t\t$r = new ReflectionObject($parent);\n\n\t\t\tforeach ($r->getMethods() as $method)\n\t\t\t{\n\t\t\t\tif ($method->isPublic())\n\t\t\t\t{\n\t\t\t\t\t$this->_methods[] = $method->getName();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tforeach ($r->getProperties() as $prop)\n\t\t\t{\n\t\t\t\tif ($prop->isPublic())\n\t\t\t\t{\n\t\t\t\t\t$this->_properties[] = $prop->getName();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tself::$_reflections[$class_name] = array($this->_methods, $this->_properties);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlist($this->_methods, $this->_properties) = self::$_reflections[$class_name];\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * __call magic method\n\t *\n\t * Handles access to the parent driver library's methods\n\t *\n\t * @param\tstring\n\t * @param\tarray\n\t * @return\tmixed\n\t */\n\tpublic function __call($method, $args = array())\n\t{\n\t\tif (in_array($method, $this->_methods))\n\t\t{\n\t\t\treturn call_user_func_array(array($this->_parent, $method), $args);\n\t\t}\n\n\t\tthrow new BadMethodCallException('No such method: '.$method.'()');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * __get magic method\n\t *\n\t * Handles reading of the parent driver library's properties\n\t *\n\t * @param\tstring\n\t * @return\tmixed\n\t */\n\tpublic function __get($var)\n\t{\n\t\tif (in_array($var, $this->_properties))\n\t\t{\n\t\t\treturn $this->_parent->$var;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * __set magic method\n\t *\n\t * Handles writing to the parent driver library's properties\n\t *\n\t * @param\tstring\n\t * @param\tarray\n\t * @return\tmixed\n\t */\n\tpublic function __set($var, $val)\n\t{\n\t\tif (in_array($var, $this->_properties))\n\t\t{\n\t\t\t$this->_parent->$var = $val;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "system/libraries/Email.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Email Class\n *\n * Permits email to be sent using Mail, Sendmail, or SMTP.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tLibraries\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/email.html\n */\nclass CI_Email {\n\n\t/**\n\t * Used as the User-Agent and X-Mailer headers' value.\n\t *\n\t * @var\tstring\n\t */\n\tpublic $useragent\t= 'CodeIgniter';\n\n\t/**\n\t * Path to the Sendmail binary.\n\t *\n\t * @var\tstring\n\t */\n\tpublic $mailpath\t= '/usr/sbin/sendmail';\t// Sendmail path\n\n\t/**\n\t * Which method to use for sending e-mails.\n\t *\n\t * @var\tstring\t'mail', 'sendmail' or 'smtp'\n\t */\n\tpublic $protocol\t= 'mail';\t\t// mail/sendmail/smtp\n\n\t/**\n\t * STMP Server host\n\t *\n\t * @var\tstring\n\t */\n\tpublic $smtp_host\t= '';\n\n\t/**\n\t * SMTP Username\n\t *\n\t * @var\tstring\n\t */\n\tpublic $smtp_user\t= '';\n\n\t/**\n\t * SMTP Password\n\t *\n\t * @var\tstring\n\t */\n\tpublic $smtp_pass\t= '';\n\n\t/**\n\t * SMTP Server port\n\t *\n\t * @var\tint\n\t */\n\tpublic $smtp_port\t= 25;\n\n\t/**\n\t * SMTP connection timeout in seconds\n\t *\n\t * @var\tint\n\t */\n\tpublic $smtp_timeout\t= 5;\n\n\t/**\n\t * SMTP persistent connection\n\t *\n\t * @var\tbool\n\t */\n\tpublic $smtp_keepalive\t= FALSE;\n\n\t/**\n\t * SMTP Encryption\n\t *\n\t * @var\tstring\tempty, 'tls' or 'ssl'\n\t */\n\tpublic $smtp_crypto\t= '';\n\n\t/**\n\t * Whether to apply word-wrapping to the message body.\n\t *\n\t * @var\tbool\n\t */\n\tpublic $wordwrap\t= TRUE;\n\n\t/**\n\t * Number of characters to wrap at.\n\t *\n\t * @see\tCI_Email::$wordwrap\n\t * @var\tint\n\t */\n\tpublic $wrapchars\t= 76;\n\n\t/**\n\t * Message format.\n\t *\n\t * @var\tstring\t'text' or 'html'\n\t */\n\tpublic $mailtype\t= 'text';\n\n\t/**\n\t * Character set (default: utf-8)\n\t *\n\t * @var\tstring\n\t */\n\tpublic $charset\t\t= 'utf-8';\n\n\t/**\n\t * Alternative message (for HTML messages only)\n\t *\n\t * @var\tstring\n\t */\n\tpublic $alt_message\t= '';\n\n\t/**\n\t * Whether to validate e-mail addresses.\n\t *\n\t * @var\tbool\n\t */\n\tpublic $validate\t= TRUE;\n\n\t/**\n\t * X-Priority header value.\n\t *\n\t * @var\tint\t1-5\n\t */\n\tpublic $priority\t= 3;\t\t\t// Default priority (1 - 5)\n\n\t/**\n\t * Newline character sequence.\n\t * Use \"\\r\\n\" to comply with RFC 822.\n\t *\n\t * @link\thttps://www.ietf.org/rfc/rfc822.txt\n\t * @var\tstring\t\"\\r\\n\" or \"\\n\"\n\t */\n\tpublic $newline\t\t= \"\\n\";\t\t\t// Default newline. \"\\r\\n\" or \"\\n\" (Use \"\\r\\n\" to comply with RFC 822)\n\n\t/**\n\t * CRLF character sequence\n\t *\n\t * RFC 2045 specifies that for 'quoted-printable' encoding,\n\t * \"\\r\\n\" must be used. However, it appears that some servers\n\t * (even on the receiving end) don't handle it properly and\n\t * switching to \"\\n\", while improper, is the only solution\n\t * that seems to work for all environments.\n\t *\n\t * @link\thttps://www.ietf.org/rfc/rfc822.txt\n\t * @var\tstring\n\t */\n\tpublic $crlf\t\t= \"\\n\";\n\n\t/**\n\t * Whether to use Delivery Status Notification.\n\t *\n\t * @var\tbool\n\t */\n\tpublic $dsn\t\t= FALSE;\n\n\t/**\n\t * Whether to send multipart alternatives.\n\t * Yahoo! doesn't seem to like these.\n\t *\n\t * @var\tbool\n\t */\n\tpublic $send_multipart\t= TRUE;\n\n\t/**\n\t * Whether to send messages to BCC recipients in batches.\n\t *\n\t * @var\tbool\n\t */\n\tpublic $bcc_batch_mode\t= FALSE;\n\n\t/**\n\t * BCC Batch max number size.\n\t *\n\t * @see\tCI_Email::$bcc_batch_mode\n\t * @var\tint\n\t */\n\tpublic $bcc_batch_size\t= 200;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Subject header\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_subject\t\t= '';\n\n\t/**\n\t * Message body\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_body\t\t= '';\n\n\t/**\n\t * Final message body to be sent.\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_finalbody\t\t= '';\n\n\t/**\n\t * Final headers to send\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_header_str\t\t= '';\n\n\t/**\n\t * SMTP Connection socket placeholder\n\t *\n\t * @var\tresource\n\t */\n\tprotected $_smtp_connect\t= '';\n\n\t/**\n\t * Mail encoding\n\t *\n\t * @var\tstring\t'8bit' or '7bit'\n\t */\n\tprotected $_encoding\t\t= '8bit';\n\n\t/**\n\t * Whether to perform SMTP authentication\n\t *\n\t * @var\tbool\n\t */\n\tprotected $_smtp_auth\t\t= FALSE;\n\n\t/**\n\t * Whether to send a Reply-To header\n\t *\n\t * @var\tbool\n\t */\n\tprotected $_replyto_flag\t= FALSE;\n\n\t/**\n\t * Debug messages\n\t *\n\t * @see\tCI_Email::print_debugger()\n\t * @var\tstring\n\t */\n\tprotected $_debug_msg\t\t= array();\n\n\t/**\n\t * Recipients\n\t *\n\t * @var\tstring[]\n\t */\n\tprotected $_recipients\t\t= array();\n\n\t/**\n\t * CC Recipients\n\t *\n\t * @var\tstring[]\n\t */\n\tprotected $_cc_array\t\t= array();\n\n\t/**\n\t * BCC Recipients\n\t *\n\t * @var\tstring[]\n\t */\n\tprotected $_bcc_array\t\t= array();\n\n\t/**\n\t * Message headers\n\t *\n\t * @var\tstring[]\n\t */\n\tprotected $_headers\t\t= array();\n\n\t/**\n\t * Attachment data\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_attachments\t\t= array();\n\n\t/**\n\t * Valid $protocol values\n\t *\n\t * @see\tCI_Email::$protocol\n\t * @var\tstring[]\n\t */\n\tprotected $_protocols\t\t= array('mail', 'sendmail', 'smtp');\n\n\t/**\n\t * Base charsets\n\t *\n\t * Character sets valid for 7-bit encoding,\n\t * excluding language suffix.\n\t *\n\t * @var\tstring[]\n\t */\n\tprotected $_base_charsets\t= array('us-ascii', 'iso-2022-');\n\n\t/**\n\t * Bit depths\n\t *\n\t * Valid mail encodings\n\t *\n\t * @see\tCI_Email::$_encoding\n\t * @var\tstring[]\n\t */\n\tprotected $_bit_depths\t\t= array('7bit', '8bit');\n\n\t/**\n\t * $priority translations\n\t *\n\t * Actual values to send with the X-Priority header\n\t *\n\t * @var\tstring[]\n\t */\n\tprotected $_priorities = array(\n\t\t1 => '1 (Highest)',\n\t\t2 => '2 (High)',\n\t\t3 => '3 (Normal)',\n\t\t4 => '4 (Low)',\n\t\t5 => '5 (Lowest)'\n\t);\n\n\t/**\n\t * mbstring.func_overload flag\n\t *\n\t * @var\tbool\n\t */\n\tprotected static $func_overload;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Constructor - Sets Email Preferences\n\t *\n\t * The constructor can be passed an array of config values\n\t *\n\t * @param\tarray\t$config = array()\n\t * @return\tvoid\n\t */\n\tpublic function __construct(array $config = array())\n\t{\n\t\t$this->charset = config_item('charset');\n\t\t$this->initialize($config);\n\n\t\tisset(self::$func_overload) OR self::$func_overload = ( ! is_php('8.0') && extension_loaded('mbstring') && @ini_get('mbstring.func_overload'));\n\n\t\tlog_message('info', 'Email Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Initialize preferences\n\t *\n\t * @param\tarray\t$config\n\t * @return\tCI_Email\n\t */\n\tpublic function initialize(array $config = array())\n\t{\n\t\t$this->clear();\n\n\t\tforeach ($config as $key => $val)\n\t\t{\n\t\t\tif (isset($this->$key))\n\t\t\t{\n\t\t\t\t$method = 'set_'.$key;\n\n\t\t\t\tif (method_exists($this, $method))\n\t\t\t\t{\n\t\t\t\t\t$this->$method($val);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$this->$key = $val;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$this->charset = strtoupper($this->charset);\n\t\t$this->_smtp_auth = isset($this->smtp_user[0], $this->smtp_pass[0]);\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Initialize the Email Data\n\t *\n\t * @param\tbool\n\t * @return\tCI_Email\n\t */\n\tpublic function clear($clear_attachments = FALSE)\n\t{\n\t\t$this->_subject\t\t= '';\n\t\t$this->_body\t\t= '';\n\t\t$this->_finalbody\t= '';\n\t\t$this->_header_str\t= '';\n\t\t$this->_replyto_flag\t= FALSE;\n\t\t$this->_recipients\t= array();\n\t\t$this->_cc_array\t= array();\n\t\t$this->_bcc_array\t= array();\n\t\t$this->_headers\t\t= array();\n\t\t$this->_debug_msg\t= array();\n\n\t\t$this->set_header('Date', $this->_set_date());\n\n\t\tif ($clear_attachments !== FALSE)\n\t\t{\n\t\t\t$this->_attachments = array();\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set FROM\n\t *\n\t * @param\tstring\t$from\n\t * @param\tstring\t$name\n\t * @param\tstring\t$return_path = NULL\tReturn-Path\n\t * @return\tCI_Email\n\t */\n\tpublic function from($from, $name = '', $return_path = NULL)\n\t{\n\t\tif (preg_match('/\\<(.*)\\>/', $from, $match))\n\t\t{\n\t\t\t$from = $match[1];\n\t\t}\n\n\t\tif ($this->validate)\n\t\t{\n\t\t\t$this->validate_email($this->_str_to_array($from));\n\t\t\tif ($return_path)\n\t\t\t{\n\t\t\t\t$this->validate_email($this->_str_to_array($return_path));\n\t\t\t}\n\t\t}\n\n\t\t// prepare the display name\n\t\tif ($name !== '')\n\t\t{\n\t\t\t// only use Q encoding if there are characters that would require it\n\t\t\tif ( ! preg_match('/[\\200-\\377]/', $name))\n\t\t\t{\n\t\t\t\t// add slashes for non-printing characters, slashes, and double quotes, and surround it in double quotes\n\t\t\t\t$name = '\"'.addcslashes($name, \"\\0..\\37\\177'\\\"\\\\\").'\"';\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$name = $this->_prep_q_encoding($name);\n\t\t\t}\n\t\t}\n\n\t\t$this->set_header('From', $name.' <'.$from.'>');\n\n\t\tisset($return_path) OR $return_path = $from;\n\t\t$this->set_header('Return-Path', '<'.$return_path.'>');\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Reply-to\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tCI_Email\n\t */\n\tpublic function reply_to($replyto, $name = '')\n\t{\n\t\tif (preg_match('/\\<(.*)\\>/', $replyto, $match))\n\t\t{\n\t\t\t$replyto = $match[1];\n\t\t}\n\n\t\tif ($this->validate)\n\t\t{\n\t\t\t$this->validate_email($this->_str_to_array($replyto));\n\t\t}\n\n\t\tif ($name !== '')\n\t\t{\n\t\t\t// only use Q encoding if there are characters that would require it\n\t\t\tif ( ! preg_match('/[\\200-\\377]/', $name))\n\t\t\t{\n\t\t\t\t// add slashes for non-printing characters, slashes, and double quotes, and surround it in double quotes\n\t\t\t\t$name = '\"'.addcslashes($name, \"\\0..\\37\\177'\\\"\\\\\").'\"';\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$name = $this->_prep_q_encoding($name);\n\t\t\t}\n\t\t}\n\n\t\t$this->set_header('Reply-To', $name.' <'.$replyto.'>');\n\t\t$this->_replyto_flag = TRUE;\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Recipients\n\t *\n\t * @param\tstring\n\t * @return\tCI_Email\n\t */\n\tpublic function to($to)\n\t{\n\t\t$to = $this->_str_to_array($to);\n\t\t$to = $this->clean_email($to);\n\n\t\tif ($this->validate)\n\t\t{\n\t\t\t$this->validate_email($to);\n\t\t}\n\n\t\tif ($this->_get_protocol() !== 'mail')\n\t\t{\n\t\t\t$this->set_header('To', implode(', ', $to));\n\t\t}\n\n\t\t$this->_recipients = $to;\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set CC\n\t *\n\t * @param\tstring\n\t * @return\tCI_Email\n\t */\n\tpublic function cc($cc)\n\t{\n\t\t$cc = $this->clean_email($this->_str_to_array($cc));\n\n\t\tif ($this->validate)\n\t\t{\n\t\t\t$this->validate_email($cc);\n\t\t}\n\n\t\t$this->set_header('Cc', implode(', ', $cc));\n\n\t\tif ($this->_get_protocol() === 'smtp')\n\t\t{\n\t\t\t$this->_cc_array = $cc;\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set BCC\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tCI_Email\n\t */\n\tpublic function bcc($bcc, $limit = '')\n\t{\n\t\tif ($limit !== '' && is_numeric($limit))\n\t\t{\n\t\t\t$this->bcc_batch_mode = TRUE;\n\t\t\t$this->bcc_batch_size = $limit;\n\t\t}\n\n\t\t$bcc = $this->clean_email($this->_str_to_array($bcc));\n\n\t\tif ($this->validate)\n\t\t{\n\t\t\t$this->validate_email($bcc);\n\t\t}\n\n\t\tif ($this->_get_protocol() === 'smtp' OR ($this->bcc_batch_mode && count($bcc) > $this->bcc_batch_size))\n\t\t{\n\t\t\t$this->_bcc_array = $bcc;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$this->set_header('Bcc', implode(', ', $bcc));\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Email Subject\n\t *\n\t * @param\tstring\n\t * @return\tCI_Email\n\t */\n\tpublic function subject($subject)\n\t{\n\t\t$subject = $this->_prep_q_encoding($subject);\n\t\t$this->set_header('Subject', $subject);\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Body\n\t *\n\t * @param\tstring\n\t * @return\tCI_Email\n\t */\n\tpublic function message($body)\n\t{\n\t\t$this->_body = rtrim(str_replace(\"\\r\", '', $body));\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Assign file attachments\n\t *\n\t * @param\tstring\t$file\tCan be local path, URL or buffered content\n\t * @param\tstring\t$disposition = 'attachment'\n\t * @param\tstring\t$newname = NULL\n\t * @param\tstring\t$mime = ''\n\t * @return\tCI_Email\n\t */\n\tpublic function attach($file, $disposition = '', $newname = NULL, $mime = '')\n\t{\n\t\tif ($mime === '')\n\t\t{\n\t\t\tif (strpos($file, '://') === FALSE && ! file_exists($file))\n\t\t\t{\n\t\t\t\t$this->_set_error_message('lang:email_attachment_missing', $file);\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\tif ( ! $fp = @fopen($file, 'rb'))\n\t\t\t{\n\t\t\t\t$this->_set_error_message('lang:email_attachment_unreadable', $file);\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\t$file_content = stream_get_contents($fp);\n\t\t\t$mime = $this->_mime_types(pathinfo($file, PATHINFO_EXTENSION));\n\t\t\tfclose($fp);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$file_content =& $file; // buffered file\n\t\t}\n\n\t\t$this->_attachments[] = array(\n\t\t\t'name'\t\t=> array($file, $newname),\n\t\t\t'disposition'\t=> empty($disposition) ? 'attachment' : $disposition,  // Can also be 'inline'  Not sure if it matters\n\t\t\t'type'\t\t=> $mime,\n\t\t\t'content'\t=> chunk_split(base64_encode($file_content)),\n\t\t\t'multipart'\t=> 'mixed'\n\t\t);\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set and return attachment Content-ID\n\t *\n\t * Useful for attached inline pictures\n\t *\n\t * @param\tstring\t$filename\n\t * @return\tstring\n\t */\n\tpublic function attachment_cid($filename)\n\t{\n\t\tfor ($i = 0, $c = count($this->_attachments); $i < $c; $i++)\n\t\t{\n\t\t\tif ($this->_attachments[$i]['name'][0] === $filename)\n\t\t\t{\n\t\t\t\t$this->_attachments[$i]['multipart'] = 'related';\n\t\t\t\t$this->_attachments[$i]['cid'] = uniqid(basename($this->_attachments[$i]['name'][0]).'@');\n\t\t\t\treturn $this->_attachments[$i]['cid'];\n\t\t\t}\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Add a Header Item\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tCI_Email\n\t */\n\tpublic function set_header($header, $value)\n\t{\n\t\t$this->_headers[$header] = str_replace(array(\"\\n\", \"\\r\"), '', $value);\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Convert a String to an Array\n\t *\n\t * @param\tstring\n\t * @return\tarray\n\t */\n\tprotected function _str_to_array($email)\n\t{\n\t\tif ( ! is_array($email))\n\t\t{\n\t\t\treturn (strpos($email, ',') !== FALSE)\n\t\t\t\t? preg_split('/[\\s,]/', $email, -1, PREG_SPLIT_NO_EMPTY)\n\t\t\t\t: (array) trim($email);\n\t\t}\n\n\t\treturn $email;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Multipart Value\n\t *\n\t * @param\tstring\n\t * @return\tCI_Email\n\t */\n\tpublic function set_alt_message($str)\n\t{\n\t\t$this->alt_message = (string) $str;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Mailtype\n\t *\n\t * @param\tstring\n\t * @return\tCI_Email\n\t */\n\tpublic function set_mailtype($type = 'text')\n\t{\n\t\t$this->mailtype = ($type === 'html') ? 'html' : 'text';\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Wordwrap\n\t *\n\t * @param\tbool\n\t * @return\tCI_Email\n\t */\n\tpublic function set_wordwrap($wordwrap = TRUE)\n\t{\n\t\t$this->wordwrap = (bool) $wordwrap;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Protocol\n\t *\n\t * @param\tstring\n\t * @return\tCI_Email\n\t */\n\tpublic function set_protocol($protocol = 'mail')\n\t{\n\t\t$this->protocol = in_array($protocol, $this->_protocols, TRUE) ? strtolower($protocol) : 'mail';\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Priority\n\t *\n\t * @param\tint\n\t * @return\tCI_Email\n\t */\n\tpublic function set_priority($n = 3)\n\t{\n\t\t$this->priority = preg_match('/^[1-5]$/', $n) ? (int) $n : 3;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Newline Character\n\t *\n\t * @param\tstring\n\t * @return\tCI_Email\n\t */\n\tpublic function set_newline($newline = \"\\n\")\n\t{\n\t\t$this->newline = in_array($newline, array(\"\\n\", \"\\r\\n\", \"\\r\")) ? $newline : \"\\n\";\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set CRLF\n\t *\n\t * @param\tstring\n\t * @return\tCI_Email\n\t */\n\tpublic function set_crlf($crlf = \"\\n\")\n\t{\n\t\t$this->crlf = ($crlf !== \"\\n\" && $crlf !== \"\\r\\n\" && $crlf !== \"\\r\") ? \"\\n\" : $crlf;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get the Message ID\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _get_message_id()\n\t{\n\t\t$from = str_replace(array('>', '<'), '', $this->_headers['Return-Path']);\n\t\treturn '<'.uniqid('').strstr($from, '@').'>';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get Mail Protocol\n\t *\n\t * @return\tmixed\n\t */\n\tprotected function _get_protocol()\n\t{\n\t\t$this->protocol = strtolower($this->protocol);\n\t\tin_array($this->protocol, $this->_protocols, TRUE) OR $this->protocol = 'mail';\n\t\treturn $this->protocol;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get Mail Encoding\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _get_encoding()\n\t{\n\t\tin_array($this->_encoding, $this->_bit_depths) OR $this->_encoding = '8bit';\n\n\t\tforeach ($this->_base_charsets as $charset)\n\t\t{\n\t\t\tif (strpos($this->charset, $charset) === 0)\n\t\t\t{\n\t\t\t\t$this->_encoding = '7bit';\n\t\t\t}\n\t\t}\n\n\t\treturn $this->_encoding;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get content type (text/html/attachment)\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _get_content_type()\n\t{\n\t\tif ($this->mailtype === 'html')\n\t\t{\n\t\t\treturn empty($this->_attachments) ? 'html' : 'html-attach';\n\t\t}\n\t\telseif\t($this->mailtype === 'text' && ! empty($this->_attachments))\n\t\t{\n\t\t\treturn 'plain-attach';\n\t\t}\n\n\t\treturn 'plain';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set RFC 822 Date\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _set_date()\n\t{\n\t\t$timezone = date('Z');\n\t\t$operator = ($timezone[0] === '-') ? '-' : '+';\n\t\t$timezone = abs($timezone);\n\t\t$timezone = floor($timezone/3600) * 100 + ($timezone % 3600) / 60;\n\n\t\treturn sprintf('%s %s%04d', date('D, j M Y H:i:s'), $operator, $timezone);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Mime message\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _get_mime_message()\n\t{\n\t\treturn 'This is a multi-part message in MIME format.'.$this->newline.'Your email application may not support this format.';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Validate Email Address\n\t *\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function validate_email($email)\n\t{\n\t\tif ( ! is_array($email))\n\t\t{\n\t\t\t$this->_set_error_message('lang:email_must_be_array');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tforeach ($email as $val)\n\t\t{\n\t\t\tif ( ! $this->valid_email($val))\n\t\t\t{\n\t\t\t\t$this->_set_error_message('lang:email_invalid_address', $val);\n\t\t\t\treturn FALSE;\n\t\t\t}\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Email Validation\n\t *\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function valid_email($email)\n\t{\n\t\tif (function_exists('idn_to_ascii') && preg_match('#\\A([^@]+)@(.+)\\z#', $email, $matches))\n\t\t{\n\t\t\t$domain = defined('INTL_IDNA_VARIANT_UTS46')\n\t\t\t\t? idn_to_ascii($matches[2], 0, INTL_IDNA_VARIANT_UTS46)\n\t\t\t\t: idn_to_ascii($matches[2]);\n\n\t\t\tif ($domain !== FALSE)\n\t\t\t{\n\t\t\t\t$email = $matches[1].'@'.$domain;\n\t\t\t}\n\t\t}\n\n\t\treturn (bool) filter_var($email, FILTER_VALIDATE_EMAIL);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Clean Extended Email Address: Joe Smith <joe@smith.com>\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tpublic function clean_email($email)\n\t{\n\t\tif ( ! is_array($email))\n\t\t{\n\t\t\treturn preg_match('/\\<(.*)\\>/', $email, $match) ? $match[1] : $email;\n\t\t}\n\n\t\t$clean_email = array();\n\n\t\tforeach ($email as $addy)\n\t\t{\n\t\t\t$clean_email[] = preg_match('/\\<(.*)\\>/', $addy, $match) ? $match[1] : $addy;\n\t\t}\n\n\t\treturn $clean_email;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Build alternative plain text message\n\t *\n\t * Provides the raw message for use in plain-text headers of\n\t * HTML-formatted emails.\n\t * If the user hasn't specified his own alternative message\n\t * it creates one by stripping the HTML\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _get_alt_message()\n\t{\n\t\tif ( ! empty($this->alt_message))\n\t\t{\n\t\t\treturn ($this->wordwrap)\n\t\t\t\t? $this->word_wrap($this->alt_message, 76)\n\t\t\t\t: $this->alt_message;\n\t\t}\n\n\t\t$body = preg_match('/\\<body.*?\\>(.*)\\<\\/body\\>/si', $this->_body, $match) ? $match[1] : $this->_body;\n\t\t$body = str_replace(\"\\t\", '', preg_replace('#<!--(.*)--\\>#', '', trim(strip_tags($body))));\n\n\t\tfor ($i = 20; $i >= 3; $i--)\n\t\t{\n\t\t\t$body = str_replace(str_repeat(\"\\n\", $i), \"\\n\\n\", $body);\n\t\t}\n\n\t\t// Reduce multiple spaces\n\t\t$body = preg_replace('| +|', ' ', $body);\n\n\t\treturn ($this->wordwrap)\n\t\t\t? $this->word_wrap($body, 76)\n\t\t\t: $body;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Word Wrap\n\t *\n\t * @param\tstring\n\t * @param\tint\tline-length limit\n\t * @return\tstring\n\t */\n\tpublic function word_wrap($str, $charlim = NULL)\n\t{\n\t\t// Set the character limit, if not already present\n\t\tif (empty($charlim))\n\t\t{\n\t\t\t$charlim = empty($this->wrapchars) ? 76 : $this->wrapchars;\n\t\t}\n\n\t\t// Standardize newlines\n\t\tif (strpos($str, \"\\r\") !== FALSE)\n\t\t{\n\t\t\t$str = str_replace(array(\"\\r\\n\", \"\\r\"), \"\\n\", $str);\n\t\t}\n\n\t\t// Reduce multiple spaces at end of line\n\t\t$str = preg_replace('| +\\n|', \"\\n\", $str);\n\n\t\t// If the current word is surrounded by {unwrap} tags we'll\n\t\t// strip the entire chunk and replace it with a marker.\n\t\t$unwrap = array();\n\t\tif (preg_match_all('|\\{unwrap\\}(.+?)\\{/unwrap\\}|s', $str, $matches))\n\t\t{\n\t\t\tfor ($i = 0, $c = count($matches[0]); $i < $c; $i++)\n\t\t\t{\n\t\t\t\t$unwrap[] = $matches[1][$i];\n\t\t\t\t$str = str_replace($matches[0][$i], '{{unwrapped'.$i.'}}', $str);\n\t\t\t}\n\t\t}\n\n\t\t// Use PHP's native function to do the initial wordwrap.\n\t\t// We set the cut flag to FALSE so that any individual words that are\n\t\t// too long get left alone. In the next step we'll deal with them.\n\t\t$str = wordwrap($str, $charlim, \"\\n\", FALSE);\n\n\t\t// Split the string into individual lines of text and cycle through them\n\t\t$output = '';\n\t\tforeach (explode(\"\\n\", $str) as $line)\n\t\t{\n\t\t\t// Is the line within the allowed character count?\n\t\t\t// If so we'll join it to the output and continue\n\t\t\tif (self::strlen($line) <= $charlim)\n\t\t\t{\n\t\t\t\t$output .= $line.$this->newline;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$temp = '';\n\t\t\tdo\n\t\t\t{\n\t\t\t\t// If the over-length word is a URL we won't wrap it\n\t\t\t\tif (preg_match('!\\[url.+\\]|://|www\\.!', $line))\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t// Trim the word down\n\t\t\t\t$temp .= self::substr($line, 0, $charlim - 1);\n\t\t\t\t$line = self::substr($line, $charlim - 1);\n\t\t\t}\n\t\t\twhile (self::strlen($line) > $charlim);\n\n\t\t\t// If $temp contains data it means we had to split up an over-length\n\t\t\t// word into smaller chunks so we'll add it back to our current line\n\t\t\tif ($temp !== '')\n\t\t\t{\n\t\t\t\t$output .= $temp.$this->newline;\n\t\t\t}\n\n\t\t\t$output .= $line.$this->newline;\n\t\t}\n\n\t\t// Put our markers back\n\t\tif (count($unwrap) > 0)\n\t\t{\n\t\t\tforeach ($unwrap as $key => $val)\n\t\t\t{\n\t\t\t\t$output = str_replace('{{unwrapped'.$key.'}}', $val, $output);\n\t\t\t}\n\t\t}\n\n\t\treturn $output;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Build final headers\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _build_headers()\n\t{\n\t\t$this->set_header('User-Agent', $this->useragent);\n\t\t$this->set_header('X-Sender', $this->clean_email($this->_headers['From']));\n\t\t$this->set_header('X-Mailer', $this->useragent);\n\t\t$this->set_header('X-Priority', $this->_priorities[$this->priority]);\n\t\t$this->set_header('Message-ID', $this->_get_message_id());\n\t\t$this->set_header('Mime-Version', '1.0');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Write Headers as a string\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _write_headers()\n\t{\n\t\tif ($this->protocol === 'mail')\n\t\t{\n\t\t\tif (isset($this->_headers['Subject']))\n\t\t\t{\n\t\t\t\t$this->_subject = $this->_headers['Subject'];\n\t\t\t\tunset($this->_headers['Subject']);\n\t\t\t}\n\t\t}\n\n\t\treset($this->_headers);\n\t\t$this->_header_str = '';\n\n\t\tforeach ($this->_headers as $key => $val)\n\t\t{\n\t\t\t$val = trim($val);\n\n\t\t\tif ($val !== '')\n\t\t\t{\n\t\t\t\t$this->_header_str .= $key.': '.$val.$this->newline;\n\t\t\t}\n\t\t}\n\n\t\tif ($this->_get_protocol() === 'mail')\n\t\t{\n\t\t\t$this->_header_str = rtrim($this->_header_str);\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Build Final Body and attachments\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _build_message()\n\t{\n\t\tif ($this->wordwrap === TRUE && $this->mailtype !== 'html')\n\t\t{\n\t\t\t$this->_body = $this->word_wrap($this->_body);\n\t\t}\n\n\t\t$this->_write_headers();\n\n\t\t$hdr = ($this->_get_protocol() === 'mail') ? $this->newline : '';\n\t\t$body = '';\n\n\t\tswitch ($this->_get_content_type())\n\t\t{\n\t\t\tcase 'plain':\n\n\t\t\t\t$hdr .= 'Content-Type: text/plain; charset='.$this->charset.$this->newline\n\t\t\t\t\t.'Content-Transfer-Encoding: '.$this->_get_encoding();\n\n\t\t\t\tif ($this->_get_protocol() === 'mail')\n\t\t\t\t{\n\t\t\t\t\t$this->_header_str .= $hdr;\n\t\t\t\t\t$this->_finalbody = $this->_body;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$this->_finalbody = $hdr.$this->newline.$this->newline.$this->_body;\n\t\t\t\t}\n\n\t\t\t\treturn;\n\n\t\t\tcase 'html':\n\n\t\t\t\tif ($this->send_multipart === FALSE)\n\t\t\t\t{\n\t\t\t\t\t$hdr .= 'Content-Type: text/html; charset='.$this->charset.$this->newline\n\t\t\t\t\t\t.'Content-Transfer-Encoding: quoted-printable';\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$boundary = uniqid('B_ALT_');\n\t\t\t\t\t$hdr .= 'Content-Type: multipart/alternative; boundary=\"'.$boundary.'\"';\n\n\t\t\t\t\t$body .= $this->_get_mime_message().$this->newline.$this->newline\n\t\t\t\t\t\t.'--'.$boundary.$this->newline\n\n\t\t\t\t\t\t.'Content-Type: text/plain; charset='.$this->charset.$this->newline\n\t\t\t\t\t\t.'Content-Transfer-Encoding: '.$this->_get_encoding().$this->newline.$this->newline\n\t\t\t\t\t\t.$this->_get_alt_message().$this->newline.$this->newline\n\t\t\t\t\t\t.'--'.$boundary.$this->newline\n\n\t\t\t\t\t\t.'Content-Type: text/html; charset='.$this->charset.$this->newline\n\t\t\t\t\t\t.'Content-Transfer-Encoding: quoted-printable'.$this->newline.$this->newline;\n\t\t\t\t}\n\n\t\t\t\t$this->_finalbody = $body.$this->_prep_quoted_printable($this->_body).$this->newline.$this->newline;\n\n\t\t\t\tif ($this->_get_protocol() === 'mail')\n\t\t\t\t{\n\t\t\t\t\t$this->_header_str .= $hdr;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$this->_finalbody = $hdr.$this->newline.$this->newline.$this->_finalbody;\n\t\t\t\t}\n\n\t\t\t\tif ($this->send_multipart !== FALSE)\n\t\t\t\t{\n\t\t\t\t\t$this->_finalbody .= '--'.$boundary.'--';\n\t\t\t\t}\n\n\t\t\t\treturn;\n\n\t\t\tcase 'plain-attach':\n\n\t\t\t\t$boundary = uniqid('B_ATC_');\n\t\t\t\t$hdr .= 'Content-Type: multipart/mixed; boundary=\"'.$boundary.'\"';\n\n\t\t\t\tif ($this->_get_protocol() === 'mail')\n\t\t\t\t{\n\t\t\t\t\t$this->_header_str .= $hdr;\n\t\t\t\t}\n\n\t\t\t\t$body .= $this->_get_mime_message().$this->newline\n\t\t\t\t\t.$this->newline\n\t\t\t\t\t.'--'.$boundary.$this->newline\n\t\t\t\t\t.'Content-Type: text/plain; charset='.$this->charset.$this->newline\n\t\t\t\t\t.'Content-Transfer-Encoding: '.$this->_get_encoding().$this->newline\n\t\t\t\t\t.$this->newline\n\t\t\t\t\t.$this->_body.$this->newline.$this->newline;\n\n\t\t\t\t$this->_append_attachments($body, $boundary);\n\n\t\t\t\tbreak;\n\t\t\tcase 'html-attach':\n\n\t\t\t\t$alt_boundary = uniqid('B_ALT_');\n\t\t\t\t$last_boundary = NULL;\n\n\t\t\t\tif ($this->_attachments_have_multipart('mixed'))\n\t\t\t\t{\n\t\t\t\t\t$atc_boundary = uniqid('B_ATC_');\n\t\t\t\t\t$hdr .= 'Content-Type: multipart/mixed; boundary=\"'.$atc_boundary.'\"';\n\t\t\t\t\t$last_boundary = $atc_boundary;\n\t\t\t\t}\n\n\t\t\t\tif ($this->_attachments_have_multipart('related'))\n\t\t\t\t{\n\t\t\t\t\t$rel_boundary = uniqid('B_REL_');\n\t\t\t\t\t$rel_boundary_header = 'Content-Type: multipart/related; boundary=\"'.$rel_boundary.'\"';\n\n\t\t\t\t\tif (isset($last_boundary))\n\t\t\t\t\t{\n\t\t\t\t\t\t$body .= '--'.$last_boundary.$this->newline.$rel_boundary_header;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t$hdr .= $rel_boundary_header;\n\t\t\t\t\t}\n\n\t\t\t\t\t$last_boundary = $rel_boundary;\n\t\t\t\t}\n\n\t\t\t\tif ($this->_get_protocol() === 'mail')\n\t\t\t\t{\n\t\t\t\t\t$this->_header_str .= $hdr;\n\t\t\t\t}\n\n\t\t\t\tself::strlen($body) && $body .= $this->newline.$this->newline;\n\t\t\t\t$body .= $this->_get_mime_message().$this->newline.$this->newline\n\t\t\t\t\t.'--'.$last_boundary.$this->newline\n\n\t\t\t\t\t.'Content-Type: multipart/alternative; boundary=\"'.$alt_boundary.'\"'.$this->newline.$this->newline\n\t\t\t\t\t.'--'.$alt_boundary.$this->newline\n\n\t\t\t\t\t.'Content-Type: text/plain; charset='.$this->charset.$this->newline\n\t\t\t\t\t.'Content-Transfer-Encoding: '.$this->_get_encoding().$this->newline.$this->newline\n\t\t\t\t\t.$this->_get_alt_message().$this->newline.$this->newline\n\t\t\t\t\t.'--'.$alt_boundary.$this->newline\n\n\t\t\t\t\t.'Content-Type: text/html; charset='.$this->charset.$this->newline\n\t\t\t\t\t.'Content-Transfer-Encoding: quoted-printable'.$this->newline.$this->newline\n\n\t\t\t\t\t.$this->_prep_quoted_printable($this->_body).$this->newline.$this->newline\n\t\t\t\t\t.'--'.$alt_boundary.'--'.$this->newline.$this->newline;\n\n\t\t\t\tif ( ! empty($rel_boundary))\n\t\t\t\t{\n\t\t\t\t\t$body .= $this->newline.$this->newline;\n\t\t\t\t\t$this->_append_attachments($body, $rel_boundary, 'related');\n\t\t\t\t}\n\n\t\t\t\t// multipart/mixed attachments\n\t\t\t\tif ( ! empty($atc_boundary))\n\t\t\t\t{\n\t\t\t\t\t$body .= $this->newline.$this->newline;\n\t\t\t\t\t$this->_append_attachments($body, $atc_boundary, 'mixed');\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t}\n\n\t\t$this->_finalbody = ($this->_get_protocol() === 'mail')\n\t\t\t? $body\n\t\t\t: $hdr.$this->newline.$this->newline.$body;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tprotected function _attachments_have_multipart($type)\n\t{\n\t\tforeach ($this->_attachments as &$attachment)\n\t\t{\n\t\t\tif ($attachment['multipart'] === $type)\n\t\t\t{\n\t\t\t\treturn TRUE;\n\t\t\t}\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Prepares attachment string\n\t *\n\t * @param\tstring\t$body\t\tMessage body to append to\n\t * @param\tstring\t$boundary\tMultipart boundary\n\t * @param\tstring\t$multipart\tWhen provided, only attachments of this type will be processed\n\t * @return\tstring\n\t */\n\tprotected function _append_attachments(&$body, $boundary, $multipart = null)\n\t{\n\t\tfor ($i = 0, $c = count($this->_attachments); $i < $c; $i++)\n\t\t{\n\t\t\tif (isset($multipart) && $this->_attachments[$i]['multipart'] !== $multipart)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$name = isset($this->_attachments[$i]['name'][1])\n\t\t\t\t? $this->_attachments[$i]['name'][1]\n\t\t\t\t: basename($this->_attachments[$i]['name'][0]);\n\n\t\t\t$body .= '--'.$boundary.$this->newline\n\t\t\t\t.'Content-Type: '.$this->_attachments[$i]['type'].'; name=\"'.$name.'\"'.$this->newline\n\t\t\t\t.'Content-Disposition: '.$this->_attachments[$i]['disposition'].';'.$this->newline\n\t\t\t\t.'Content-Transfer-Encoding: base64'.$this->newline\n\t\t\t\t.(empty($this->_attachments[$i]['cid']) ? '' : 'Content-ID: <'.$this->_attachments[$i]['cid'].'>'.$this->newline)\n\t\t\t\t.$this->newline\n\t\t\t\t.$this->_attachments[$i]['content'].$this->newline;\n\t\t}\n\n\t\t// $name won't be set if no attachments were appended,\n\t\t// and therefore a boundary wouldn't be necessary\n\t\tempty($name) OR $body .= '--'.$boundary.'--';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Prep Quoted Printable\n\t *\n\t * Prepares string for Quoted-Printable Content-Transfer-Encoding\n\t * Refer to RFC 2045 https://www.ietf.org/rfc/rfc2045.txt\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tprotected function _prep_quoted_printable($str)\n\t{\n\t\t// ASCII code numbers for \"safe\" characters that can always be\n\t\t// used literally, without encoding, as described in RFC 2049.\n\t\t// https://www.ietf.org/rfc/rfc2049.txt\n\t\tstatic $ascii_safe_chars = array(\n\t\t\t// ' (  )   +   ,   -   .   /   :   =   ?\n\t\t\t39, 40, 41, 43, 44, 45, 46, 47, 58, 61, 63,\n\t\t\t// numbers\n\t\t\t48, 49, 50, 51, 52, 53, 54, 55, 56, 57,\n\t\t\t// upper-case letters\n\t\t\t65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,\n\t\t\t// lower-case letters\n\t\t\t97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122\n\t\t);\n\n\t\t// We are intentionally wrapping so mail servers will encode characters\n\t\t// properly and MUAs will behave, so {unwrap} must go!\n\t\t$str = str_replace(array('{unwrap}', '{/unwrap}'), '', $str);\n\n\t\t// RFC 2045 specifies CRLF as \"\\r\\n\".\n\t\t// However, many developers choose to override that and violate\n\t\t// the RFC rules due to (apparently) a bug in MS Exchange,\n\t\t// which only works with \"\\n\".\n\t\tif ($this->crlf === \"\\r\\n\")\n\t\t{\n\t\t\treturn quoted_printable_encode($str);\n\t\t}\n\n\t\t// Reduce multiple spaces & remove nulls\n\t\t$str = preg_replace(array('| +|', '/\\x00+/'), array(' ', ''), $str);\n\n\t\t// Standardize newlines\n\t\tif (strpos($str, \"\\r\") !== FALSE)\n\t\t{\n\t\t\t$str = str_replace(array(\"\\r\\n\", \"\\r\"), \"\\n\", $str);\n\t\t}\n\n\t\t$escape = '=';\n\t\t$output = '';\n\n\t\tforeach (explode(\"\\n\", $str) as $line)\n\t\t{\n\t\t\t$length = self::strlen($line);\n\t\t\t$temp = '';\n\n\t\t\t// Loop through each character in the line to add soft-wrap\n\t\t\t// characters at the end of a line \" =\\r\\n\" and add the newly\n\t\t\t// processed line(s) to the output (see comment on $crlf class property)\n\t\t\tfor ($i = 0; $i < $length; $i++)\n\t\t\t{\n\t\t\t\t// Grab the next character\n\t\t\t\t$char = $line[$i];\n\t\t\t\t$ascii = ord($char);\n\n\t\t\t\t// Convert spaces and tabs but only if it's the end of the line\n\t\t\t\tif ($ascii === 32 OR $ascii === 9)\n\t\t\t\t{\n\t\t\t\t\tif ($i === ($length - 1))\n\t\t\t\t\t{\n\t\t\t\t\t\t$char = $escape.sprintf('%02s', dechex($ascii));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// DO NOT move this below the $ascii_safe_chars line!\n\t\t\t\t//\n\t\t\t\t// = (equals) signs are allowed by RFC2049, but must be encoded\n\t\t\t\t// as they are the encoding delimiter!\n\t\t\t\telseif ($ascii === 61)\n\t\t\t\t{\n\t\t\t\t\t$char = $escape.strtoupper(sprintf('%02s', dechex($ascii)));  // =3D\n\t\t\t\t}\n\t\t\t\telseif ( ! in_array($ascii, $ascii_safe_chars, TRUE))\n\t\t\t\t{\n\t\t\t\t\t$char = $escape.strtoupper(sprintf('%02s', dechex($ascii)));\n\t\t\t\t}\n\n\t\t\t\t// If we're at the character limit, add the line to the output,\n\t\t\t\t// reset our temp variable, and keep on chuggin'\n\t\t\t\tif ((self::strlen($temp) + self::strlen($char)) >= 76)\n\t\t\t\t{\n\t\t\t\t\t$output .= $temp.$escape.$this->crlf;\n\t\t\t\t\t$temp = '';\n\t\t\t\t}\n\n\t\t\t\t// Add the character to our temporary line\n\t\t\t\t$temp .= $char;\n\t\t\t}\n\n\t\t\t// Add our completed line to the output\n\t\t\t$output .= $temp.$this->crlf;\n\t\t}\n\n\t\t// get rid of extra CRLF tacked onto the end\n\t\treturn self::substr($output, 0, self::strlen($this->crlf) * -1);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Prep Q Encoding\n\t *\n\t * Performs \"Q Encoding\" on a string for use in email headers.\n\t * It's related but not identical to quoted-printable, so it has its\n\t * own method.\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tprotected function _prep_q_encoding($str)\n\t{\n\t\t$str = str_replace(array(\"\\r\", \"\\n\"), '', $str);\n\n\t\tif ($this->charset === 'UTF-8')\n\t\t{\n\t\t\t// Note: We used to have mb_encode_mimeheader() as the first choice\n\t\t\t//       here, but it turned out to be buggy and unreliable. DO NOT\n\t\t\t//       re-add it! -- Narf\n\t\t\tif (ICONV_ENABLED === TRUE)\n\t\t\t{\n\t\t\t\t$output = @iconv_mime_encode('', $str,\n\t\t\t\t\tarray(\n\t\t\t\t\t\t'scheme' => 'Q',\n\t\t\t\t\t\t'line-length' => 76,\n\t\t\t\t\t\t'input-charset' => $this->charset,\n\t\t\t\t\t\t'output-charset' => $this->charset,\n\t\t\t\t\t\t'line-break-chars' => $this->crlf\n\t\t\t\t\t)\n\t\t\t\t);\n\n\t\t\t\t// There are reports that iconv_mime_encode() might fail and return FALSE\n\t\t\t\tif ($output !== FALSE)\n\t\t\t\t{\n\t\t\t\t\t// iconv_mime_encode() will always put a header field name.\n\t\t\t\t\t// We've passed it an empty one, but it still prepends our\n\t\t\t\t\t// encoded string with ': ', so we need to strip it.\n\t\t\t\t\treturn self::substr($output, 2);\n\t\t\t\t}\n\n\t\t\t\t$chars = iconv_strlen($str, 'UTF-8');\n\t\t\t}\n\t\t\telseif (MB_ENABLED === TRUE)\n\t\t\t{\n\t\t\t\t$chars = mb_strlen($str, 'UTF-8');\n\t\t\t}\n\t\t}\n\n\t\t// We might already have this set for UTF-8\n\t\tisset($chars) OR $chars = self::strlen($str);\n\n\t\t$output = '=?'.$this->charset.'?Q?';\n\t\tfor ($i = 0, $length = self::strlen($output); $i < $chars; $i++)\n\t\t{\n\t\t\t$chr = ($this->charset === 'UTF-8' && ICONV_ENABLED === TRUE)\n\t\t\t\t? '='.implode('=', str_split(strtoupper(bin2hex(iconv_substr($str, $i, 1, $this->charset))), 2))\n\t\t\t\t: '='.strtoupper(bin2hex($str[$i]));\n\n\t\t\t// RFC 2045 sets a limit of 76 characters per line.\n\t\t\t// We'll append ?= to the end of each line though.\n\t\t\tif ($length + ($l = self::strlen($chr)) > 74)\n\t\t\t{\n\t\t\t\t$output .= '?='.$this->crlf // EOL\n\t\t\t\t\t.' =?'.$this->charset.'?Q?'.$chr; // New line\n\t\t\t\t$length = 6 + self::strlen($this->charset) + $l; // Reset the length for the new line\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$output .= $chr;\n\t\t\t\t$length += $l;\n\t\t\t}\n\t\t}\n\n\t\t// End the header\n\t\treturn $output.'?=';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Send Email\n\t *\n\t * @param\tbool\t$auto_clear = TRUE\n\t * @return\tbool\n\t */\n\tpublic function send($auto_clear = TRUE)\n\t{\n\t\tif ( ! isset($this->_headers['From']))\n\t\t{\n\t\t\t$this->_set_error_message('lang:email_no_from');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif ($this->_replyto_flag === FALSE)\n\t\t{\n\t\t\t$this->reply_to($this->_headers['From']);\n\t\t}\n\n\t\tif (empty($this->_recipients) && ! isset($this->_headers['To'])\n\t\t\t&& empty($this->_bcc_array) && ! isset($this->_headers['Bcc'])\n\t\t\t&& ! isset($this->_headers['Cc']))\n\t\t{\n\t\t\t$this->_set_error_message('lang:email_no_recipients');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$this->_build_headers();\n\n\t\tif ($this->bcc_batch_mode && count($this->_bcc_array) > $this->bcc_batch_size)\n\t\t{\n\t\t\t$this->batch_bcc_send();\n\n\t\t\tif ($auto_clear)\n\t\t\t{\n\t\t\t\t$this->clear();\n\t\t\t}\n\n\t\t\treturn TRUE;\n\t\t}\n\n\t\t$this->_build_message();\n\t\t$result = $this->_spool_email();\n\n\t\tif ($result && $auto_clear)\n\t\t{\n\t\t\t$this->clear();\n\t\t}\n\n\t\treturn $result;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Batch Bcc Send. Sends groups of BCCs in batches\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function batch_bcc_send()\n\t{\n\t\t$float = $this->bcc_batch_size - 1;\n\t\t$set = '';\n\t\t$chunk = array();\n\n\t\tfor ($i = 0, $c = count($this->_bcc_array); $i < $c; $i++)\n\t\t{\n\t\t\tif (isset($this->_bcc_array[$i]))\n\t\t\t{\n\t\t\t\t$set .= ', '.$this->_bcc_array[$i];\n\t\t\t}\n\n\t\t\tif ($i === $float)\n\t\t\t{\n\t\t\t\t$chunk[] = self::substr($set, 1);\n\t\t\t\t$float += $this->bcc_batch_size;\n\t\t\t\t$set = '';\n\t\t\t}\n\n\t\t\tif ($i === $c-1)\n\t\t\t{\n\t\t\t\t$chunk[] = self::substr($set, 1);\n\t\t\t}\n\t\t}\n\n\t\tfor ($i = 0, $c = count($chunk); $i < $c; $i++)\n\t\t{\n\t\t\tunset($this->_headers['Bcc']);\n\n\t\t\t$bcc = $this->clean_email($this->_str_to_array($chunk[$i]));\n\n\t\t\tif ($this->protocol !== 'smtp')\n\t\t\t{\n\t\t\t\t$this->set_header('Bcc', implode(', ', $bcc));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$this->_bcc_array = $bcc;\n\t\t\t}\n\n\t\t\t$this->_build_message();\n\t\t\t$this->_spool_email();\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Unwrap special elements\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _unwrap_specials()\n\t{\n\t\t$this->_finalbody = preg_replace_callback('/\\{unwrap\\}(.*?)\\{\\/unwrap\\}/si', array($this, '_remove_nl_callback'), $this->_finalbody);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Strip line-breaks via callback\n\t *\n\t * @param\tstring\t$matches\n\t * @return\tstring\n\t */\n\tprotected function _remove_nl_callback($matches)\n\t{\n\t\tif (strpos($matches[1], \"\\r\") !== FALSE OR strpos($matches[1], \"\\n\") !== FALSE)\n\t\t{\n\t\t\t$matches[1] = str_replace(array(\"\\r\\n\", \"\\r\", \"\\n\"), '', $matches[1]);\n\t\t}\n\n\t\treturn $matches[1];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Spool mail to the mail server\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _spool_email()\n\t{\n\t\t$this->_unwrap_specials();\n\n\t\t$protocol = $this->_get_protocol();\n\t\t$method   = '_send_with_'.$protocol;\n\t\tif ( ! $this->$method())\n\t\t{\n\t\t\t$this->_set_error_message('lang:email_send_failure_'.($protocol === 'mail' ? 'phpmail' : $protocol));\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$this->_set_error_message('lang:email_sent', $protocol);\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Validate email for shell\n\t *\n\t * Applies stricter, shell-safe validation to email addresses.\n\t * Introduced to prevent RCE via sendmail's -f option.\n\t *\n\t * @see\thttps://github.com/bcit-ci/CodeIgniter/issues/4963\n\t * @see\thttps://gist.github.com/Zenexer/40d02da5e07f151adeaeeaa11af9ab36\n\t * @license\thttps://creativecommons.org/publicdomain/zero/1.0/\tCC0 1.0, Public Domain\n\t *\n\t * Credits for the base concept go to Paul Buonopane <paul@namepros.com>\n\t *\n\t * @param\tstring\t$email\n\t * @return\tbool\n\t */\n\tprotected function _validate_email_for_shell(&$email)\n\t{\n\t\tif (function_exists('idn_to_ascii') && $atpos = strpos($email, '@'))\n\t\t{\n\t\t\tlist($account, $domain) = explode('@', $email, 2);\n\t\t\t$domain = defined('INTL_IDNA_VARIANT_UTS46')\n\t\t\t\t? idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46)\n\t\t\t\t: idn_to_ascii($domain);\n\n\t\t\tif ($domain !== FALSE)\n\t\t\t{\n\t\t\t\t$email = $account.'@'.$domain;\n\t\t\t}\n\t\t}\n\n\t\treturn (filter_var($email, FILTER_VALIDATE_EMAIL) === $email && preg_match('#\\A[a-z0-9._+-]+@[a-z0-9.-]{1,253}\\z#i', $email));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Send using mail()\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _send_with_mail()\n\t{\n\t\tif (is_array($this->_recipients))\n\t\t{\n\t\t\t$this->_recipients = implode(', ', $this->_recipients);\n\t\t}\n\n\t\t// _validate_email_for_shell() below accepts by reference,\n\t\t// so this needs to be assigned to a variable\n\t\t$from = $this->clean_email($this->_headers['Return-Path']);\n\n\t\tif ( ! $this->_validate_email_for_shell($from))\n\t\t{\n\t\t\treturn mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str);\n\t\t}\n\n\t\t// most documentation of sendmail using the \"-f\" flag lacks a space after it, however\n\t\t// we've encountered servers that seem to require it to be in place.\n\t\treturn mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str, '-f '.$from);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Send using Sendmail\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _send_with_sendmail()\n\t{\n\t\t// _validate_email_for_shell() below accepts by reference,\n\t\t// so this needs to be assigned to a variable\n\t\t$from = $this->clean_email($this->_headers['From']);\n\t\tif ($this->_validate_email_for_shell($from))\n\t\t{\n\t\t\t$from = '-f '.$from;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$from = '';\n\t\t}\n\n\t\t// is popen() enabled?\n\t\tif ( ! function_usable('popen')\tOR FALSE === ($fp = @popen($this->mailpath.' -oi '.$from.' -t', 'w')))\n\t\t{\n\t\t\t// server probably has popen disabled, so nothing we can do to get a verbose error.\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tfputs($fp, $this->_header_str);\n\t\tfputs($fp, $this->_finalbody);\n\n\t\t$status = pclose($fp);\n\n\t\tif ($status !== 0)\n\t\t{\n\t\t\t$this->_set_error_message('lang:email_exit_status', $status);\n\t\t\t$this->_set_error_message('lang:email_no_socket');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Send using SMTP\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _send_with_smtp()\n\t{\n\t\tif ($this->smtp_host === '')\n\t\t{\n\t\t\t$this->_set_error_message('lang:email_no_hostname');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif ( ! $this->_smtp_connect() OR ! $this->_smtp_authenticate())\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif ( ! $this->_send_command('from', $this->clean_email($this->_headers['From'])))\n\t\t{\n\t\t\t$this->_smtp_end();\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tforeach ($this->_recipients as $val)\n\t\t{\n\t\t\tif ( ! $this->_send_command('to', $val))\n\t\t\t{\n\t\t\t\t$this->_smtp_end();\n\t\t\t\treturn FALSE;\n\t\t\t}\n\t\t}\n\n\t\tforeach ($this->_cc_array as $val)\n\t\t{\n\t\t\tif ($val !== '' && ! $this->_send_command('to', $val))\n\t\t\t{\n\t\t\t\t$this->_smtp_end();\n\t\t\t\treturn FALSE;\n\t\t\t}\n\t\t}\n\n\t\tforeach ($this->_bcc_array as $val)\n\t\t{\n\t\t\tif ($val !== '' && ! $this->_send_command('to', $val))\n\t\t\t{\n\t\t\t\t$this->_smtp_end();\n\t\t\t\treturn FALSE;\n\t\t\t}\n\t\t}\n\n\t\tif ( ! $this->_send_command('data'))\n\t\t{\n\t\t\t$this->_smtp_end();\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// perform dot transformation on any lines that begin with a dot\n\t\t$this->_send_data($this->_header_str.preg_replace('/^\\./m', '..$1', $this->_finalbody));\n\n\t\t$this->_send_data('.');\n\t\t$reply = $this->_get_smtp_data();\n\t\t$this->_set_error_message($reply);\n\n\t\t$this->_smtp_end();\n\n\t\tif (strpos($reply, '250') !== 0)\n\t\t{\n\t\t\t$this->_set_error_message('lang:email_smtp_error', $reply);\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * SMTP End\n\t *\n\t * Shortcut to send RSET or QUIT depending on keep-alive\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _smtp_end()\n\t{\n\t\t$this->_send_command($this->smtp_keepalive ? 'reset' : 'quit');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * SMTP Connect\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _smtp_connect()\n\t{\n\t\tif (is_resource($this->_smtp_connect))\n\t\t{\n\t\t\treturn TRUE;\n\t\t}\n\n\t\t$ssl = ($this->smtp_crypto === 'ssl') ? 'ssl://' : '';\n\n\t\t$this->_smtp_connect = fsockopen(\n\t\t\t$ssl.$this->smtp_host,\n\t\t\t$this->smtp_port,\n\t\t\t$errno,\n\t\t\t$errstr,\n\t\t\t$this->smtp_timeout\n\t\t);\n\n\t\tif ( ! is_resource($this->_smtp_connect))\n\t\t{\n\t\t\t$this->_set_error_message('lang:email_smtp_error', $errno.' '.$errstr);\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tstream_set_timeout($this->_smtp_connect, $this->smtp_timeout);\n\t\t$this->_set_error_message($this->_get_smtp_data());\n\n\t\tif ($this->smtp_crypto === 'tls')\n\t\t{\n\t\t\t$this->_send_command('hello');\n\t\t\t$this->_send_command('starttls');\n\n\t\t\t/**\n\t\t\t * STREAM_CRYPTO_METHOD_TLS_CLIENT is quite the mess ...\n\t\t\t *\n\t\t\t * - On PHP <5.6 it doesn't even mean TLS, but SSL 2.0, and there's no option to use actual TLS\n\t\t\t * - On PHP 5.6.0-5.6.6, >=7.2 it means negotiation with any of TLS 1.0, 1.1, 1.2\n\t\t\t * - On PHP 5.6.7-7.1.* it means only TLS 1.0\n\t\t\t *\n\t\t\t * We want the negotiation, so we'll force it below ...\n\t\t\t */\n\t\t\t$method = is_php('5.6')\n\t\t\t\t? STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT | STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT | STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT\n\t\t\t\t: STREAM_CRYPTO_METHOD_TLS_CLIENT;\n\t\t\t$crypto = stream_socket_enable_crypto($this->_smtp_connect, TRUE, $method);\n\n\t\t\tif ($crypto !== TRUE)\n\t\t\t{\n\t\t\t\t$this->_set_error_message('lang:email_smtp_error', $this->_get_smtp_data());\n\t\t\t\treturn FALSE;\n\t\t\t}\n\t\t}\n\n\t\treturn $this->_send_command('hello');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Send SMTP command\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tprotected function _send_command($cmd, $data = '')\n\t{\n\t\tswitch ($cmd)\n\t\t{\n\t\t\tcase 'hello':\n\t\t\t\tif ($this->_smtp_auth OR $this->_get_encoding() === '8bit')\n\t\t\t\t{\n\t\t\t\t\t$this->_send_data('EHLO '.$this->_get_hostname());\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$this->_send_data('HELO '.$this->_get_hostname());\n\t\t\t\t}\n\n\t\t\t\t$resp = 250;\n\t\t\t\tbreak;\n\t\t\tcase 'starttls':\n\t\t\t\t$this->_send_data('STARTTLS');\n\t\t\t\t$resp = 220;\n\t\t\t\tbreak;\n\t\t\tcase 'from':\n\t\t\t\t$this->_send_data('MAIL FROM:<'.$data.'>');\n\t\t\t\t$resp = 250;\n\t\t\t\tbreak;\n\t\t\tcase 'to':\n\t\t\t\tif ($this->dsn)\n\t\t\t\t{\n\t\t\t\t\t$this->_send_data('RCPT TO:<'.$data.'> NOTIFY=SUCCESS,DELAY,FAILURE ORCPT=rfc822;'.$data);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$this->_send_data('RCPT TO:<'.$data.'>');\n\t\t\t\t}\n\t\t\t\t$resp = 250;\n\t\t\t\tbreak;\n\t\t\tcase 'data':\n\t\t\t\t$this->_send_data('DATA');\n\t\t\t\t$resp = 354;\n\t\t\t\tbreak;\n\t\t\tcase 'reset':\n\t\t\t\t$this->_send_data('RSET');\n\t\t\t\t$resp = 250;\n\t\t\t\tbreak;\n\t\t\tcase 'quit':\n\t\t\t\t$this->_send_data('QUIT');\n\t\t\t\t$resp = 221;\n\t\t\t\tbreak;\n\t\t}\n\n\t\t$reply = $this->_get_smtp_data();\n\n\t\t$this->_debug_msg[] = '<pre>'.$cmd.': '.$reply.'</pre>';\n\n\t\tif ((int) self::substr($reply, 0, 3) !== $resp)\n\t\t{\n\t\t\t$this->_set_error_message('lang:email_smtp_error', $reply);\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif ($cmd === 'quit')\n\t\t{\n\t\t\tfclose($this->_smtp_connect);\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * SMTP Authenticate\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _smtp_authenticate()\n\t{\n\t\tif ( ! $this->_smtp_auth)\n\t\t{\n\t\t\treturn TRUE;\n\t\t}\n\n\t\tif ($this->smtp_user === '' && $this->smtp_pass === '')\n\t\t{\n\t\t\t$this->_set_error_message('lang:email_no_smtp_unpw');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$this->_send_data('AUTH LOGIN');\n\t\t$reply = $this->_get_smtp_data();\n\n\t\tif (strpos($reply, '503') === 0)\t// Already authenticated\n\t\t{\n\t\t\treturn TRUE;\n\t\t}\n\t\telseif (strpos($reply, '334') !== 0)\n\t\t{\n\t\t\t$this->_set_error_message('lang:email_failed_smtp_login', $reply);\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$this->_send_data(base64_encode($this->smtp_user));\n\t\t$reply = $this->_get_smtp_data();\n\n\t\tif (strpos($reply, '334') !== 0)\n\t\t{\n\t\t\t$this->_set_error_message('lang:email_smtp_auth_un', $reply);\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$this->_send_data(base64_encode($this->smtp_pass));\n\t\t$reply = $this->_get_smtp_data();\n\n\t\tif (strpos($reply, '235') !== 0)\n\t\t{\n\t\t\t$this->_set_error_message('lang:email_smtp_auth_pw', $reply);\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif ($this->smtp_keepalive)\n\t\t{\n\t\t\t$this->_smtp_auth = FALSE;\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Send SMTP data\n\t *\n\t * @param\tstring\t$data\n\t * @return\tbool\n\t */\n\tprotected function _send_data($data)\n\t{\n\t\t$data .= $this->newline;\n\t\tfor ($written = $timestamp = 0, $length = self::strlen($data); $written < $length; $written += $result)\n\t\t{\n\t\t\tif (($result = fwrite($this->_smtp_connect, self::substr($data, $written))) === FALSE)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t// See https://bugs.php.net/bug.php?id=39598 and https://secure.php.net/manual/en/function.fwrite.php#96951\n\t\t\telseif ($result === 0)\n\t\t\t{\n\t\t\t\tif ($timestamp === 0)\n\t\t\t\t{\n\t\t\t\t\t$timestamp = time();\n\t\t\t\t}\n\t\t\t\telseif ($timestamp < (time() - $this->smtp_timeout))\n\t\t\t\t{\n\t\t\t\t\t$result = FALSE;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tusleep(250000);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$timestamp = 0;\n\t\t}\n\n\t\tif ($result === FALSE)\n\t\t{\n\t\t\t$this->_set_error_message('lang:email_smtp_data_failure', $data);\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get SMTP data\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _get_smtp_data()\n\t{\n\t\t$data = '';\n\n\t\twhile ($str = fgets($this->_smtp_connect, 512))\n\t\t{\n\t\t\t$data .= $str;\n\n\t\t\tif ($str[3] === ' ')\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn $data;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get Hostname\n\t *\n\t * There are only two legal types of hostname - either a fully\n\t * qualified domain name (eg: \"mail.example.com\") or an IP literal\n\t * (eg: \"[1.2.3.4]\").\n\t *\n\t * @link\thttps://tools.ietf.org/html/rfc5321#section-2.3.5\n\t * @link\thttps://cbl.abuseat.org/namingproblems.html\n\t * @return\tstring\n\t */\n\tprotected function _get_hostname()\n\t{\n\t\tif (isset($_SERVER['SERVER_NAME']))\n\t\t{\n\t\t\treturn $_SERVER['SERVER_NAME'];\n\t\t}\n\n\t\treturn isset($_SERVER['SERVER_ADDR']) ? '['.$_SERVER['SERVER_ADDR'].']' : '[127.0.0.1]';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get Debug Message\n\t *\n\t * @param\tarray\t$include\tList of raw data chunks to include in the output\n\t *\t\t\t\t\tValid options are: 'headers', 'subject', 'body'\n\t * @return\tstring\n\t */\n\tpublic function print_debugger($include = array('headers', 'subject', 'body'))\n\t{\n\t\t$msg = implode('', $this->_debug_msg);\n\n\t\t// Determine which parts of our raw data needs to be printed\n\t\t$raw_data = '';\n\t\tis_array($include) OR $include = array($include);\n\n\t\tin_array('headers', $include, TRUE) && $raw_data  = htmlspecialchars($this->_header_str).\"\\n\";\n\t\tin_array('subject', $include, TRUE) && $raw_data .= htmlspecialchars($this->_subject).\"\\n\";\n\t\tin_array('body', $include, TRUE)    && $raw_data .= htmlspecialchars($this->_finalbody);\n\n\t\treturn $msg.($raw_data === '' ? '' : '<pre>'.$raw_data.'</pre>');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Message\n\t *\n\t * @param\tstring\t$msg\n\t * @param\tstring\t$val = ''\n\t * @return\tvoid\n\t */\n\tprotected function _set_error_message($msg, $val = '')\n\t{\n\t\t$CI =& get_instance();\n\t\t$CI->lang->load('email');\n\n\t\tif (sscanf($msg, 'lang:%s', $line) !== 1 OR FALSE === ($line = $CI->lang->line($line)))\n\t\t{\n\t\t\t$this->_debug_msg[] = str_replace('%s', $val, $msg).'<br />';\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$this->_debug_msg[] = str_replace('%s', $val, $line).'<br />';\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Mime Types\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tprotected function _mime_types($ext = '')\n\t{\n\t\t$ext = strtolower($ext);\n\n\t\t$mimes =& get_mimes();\n\n\t\tif (isset($mimes[$ext]))\n\t\t{\n\t\t\treturn is_array($mimes[$ext])\n\t\t\t\t? current($mimes[$ext])\n\t\t\t\t: $mimes[$ext];\n\t\t}\n\n\t\treturn 'application/x-unknown-content-type';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Destructor\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __destruct()\n\t{\n\t\tis_resource($this->_smtp_connect) && $this->_send_command('quit');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Byte-safe strlen()\n\t *\n\t * @param\tstring\t$str\n\t * @return\tint\n\t */\n\tprotected static function strlen($str)\n\t{\n\t\treturn (self::$func_overload)\n\t\t\t? mb_strlen($str, '8bit')\n\t\t\t: strlen($str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Byte-safe substr()\n\t *\n\t * @param\tstring\t$str\n\t * @param\tint\t$start\n\t * @param\tint\t$length\n\t * @return\tstring\n\t */\n\tprotected static function substr($str, $start, $length = NULL)\n\t{\n\t\tif (self::$func_overload)\n\t\t{\n\t\t\treturn mb_substr($str, $start, $length, '8bit');\n\t\t}\n\n\t\treturn isset($length)\n\t\t\t? substr($str, $start, $length)\n\t\t\t: substr($str, $start);\n\t}\n}\n"
  },
  {
    "path": "system/libraries/Encryption.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Encryption Class\n *\n * Provides two-way keyed encryption via PHP's MCrypt and/or OpenSSL extensions.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tLibraries\n * @author\t\tAndrey Andreev\n * @link\t\thttps://codeigniter.com/userguide3/libraries/encryption.html\n */\nclass CI_Encryption {\n\n\t/**\n\t * Encryption cipher\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_cipher = 'aes-128';\n\n\t/**\n\t * Cipher mode\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_mode = 'cbc';\n\n\t/**\n\t * Cipher handle\n\t *\n\t * @var\tmixed\n\t */\n\tprotected $_handle;\n\n\t/**\n\t * Encryption key\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_key;\n\n\t/**\n\t * PHP extension to be used\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_driver;\n\n\t/**\n\t * List of usable drivers (PHP extensions)\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_drivers = array();\n\n\t/**\n\t * List of available modes\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_modes = array(\n\t\t'mcrypt' => array(\n\t\t\t'cbc' => 'cbc',\n\t\t\t'ecb' => 'ecb',\n\t\t\t'ofb' => 'nofb',\n\t\t\t'ofb8' => 'ofb',\n\t\t\t'cfb' => 'ncfb',\n\t\t\t'cfb8' => 'cfb',\n\t\t\t'ctr' => 'ctr',\n\t\t\t'stream' => 'stream'\n\t\t),\n\t\t'openssl' => array(\n\t\t\t'cbc' => 'cbc',\n\t\t\t'ecb' => 'ecb',\n\t\t\t'ofb' => 'ofb',\n\t\t\t'cfb' => 'cfb',\n\t\t\t'cfb8' => 'cfb8',\n\t\t\t'ctr' => 'ctr',\n\t\t\t'stream' => '',\n\t\t\t'xts' => 'xts'\n\t\t)\n\t);\n\n\t/**\n\t * List of supported HMAC algorithms\n\t *\n\t * name => digest size pairs\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_digests = array(\n\t\t'sha224' => 28,\n\t\t'sha256' => 32,\n\t\t'sha384' => 48,\n\t\t'sha512' => 64\n\t);\n\n\t/**\n\t * mbstring.func_overload flag\n\t *\n\t * @var\tbool\n\t */\n\tprotected static $func_overload;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * @param\tarray\t$params\tConfiguration parameters\n\t * @return\tvoid\n\t */\n\tpublic function __construct(array $params = array())\n\t{\n\t\t$this->_drivers = array(\n\t\t\t'mcrypt'  => defined('MCRYPT_DEV_URANDOM'),\n\t\t\t'openssl' => extension_loaded('openssl')\n\t\t);\n\n\t\tif ( ! $this->_drivers['mcrypt'] && ! $this->_drivers['openssl'])\n\t\t{\n\t\t\tshow_error('Encryption: Unable to find an available encryption driver.');\n\t\t}\n\n\t\tisset(self::$func_overload) OR self::$func_overload = ( ! is_php('8.0') && extension_loaded('mbstring') && @ini_get('mbstring.func_overload'));\n\t\t$this->initialize($params);\n\n\t\tif ( ! isset($this->_key) && self::strlen($key = config_item('encryption_key')) > 0)\n\t\t{\n\t\t\t$this->_key = $key;\n\t\t}\n\n\t\tlog_message('info', 'Encryption Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Initialize\n\t *\n\t * @param\tarray\t$params\tConfiguration parameters\n\t * @return\tCI_Encryption\n\t */\n\tpublic function initialize(array $params)\n\t{\n\t\tif ( ! empty($params['driver']))\n\t\t{\n\t\t\tif (isset($this->_drivers[$params['driver']]))\n\t\t\t{\n\t\t\t\tif ($this->_drivers[$params['driver']])\n\t\t\t\t{\n\t\t\t\t\t$this->_driver = $params['driver'];\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tlog_message('error', \"Encryption: Driver '\".$params['driver'].\"' is not available.\");\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tlog_message('error', \"Encryption: Unknown driver '\".$params['driver'].\"' cannot be configured.\");\n\t\t\t}\n\t\t}\n\n\t\tif (empty($this->_driver))\n\t\t{\n\t\t\t$this->_driver = ($this->_drivers['openssl'] === TRUE)\n\t\t\t\t? 'openssl'\n\t\t\t\t: 'mcrypt';\n\n\t\t\tlog_message('debug', \"Encryption: Auto-configured driver '\".$this->_driver.\"'.\");\n\t\t}\n\n\t\tempty($params['cipher']) && $params['cipher'] = $this->_cipher;\n\t\tempty($params['key']) OR $this->_key = $params['key'];\n\t\t$this->{'_'.$this->_driver.'_initialize'}($params);\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Initialize MCrypt\n\t *\n\t * @param\tarray\t$params\tConfiguration parameters\n\t * @return\tvoid\n\t */\n\tprotected function _mcrypt_initialize($params)\n\t{\n\t\tif ( ! empty($params['cipher']))\n\t\t{\n\t\t\t$params['cipher'] = strtolower($params['cipher']);\n\t\t\t$this->_cipher_alias($params['cipher']);\n\n\t\t\tif ( ! in_array($params['cipher'], mcrypt_list_algorithms(), TRUE))\n\t\t\t{\n\t\t\t\tlog_message('error', 'Encryption: MCrypt cipher '.strtoupper($params['cipher']).' is not available.');\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$this->_cipher = $params['cipher'];\n\t\t\t}\n\t\t}\n\n\t\tif ( ! empty($params['mode']))\n\t\t{\n\t\t\t$params['mode'] = strtolower($params['mode']);\n\t\t\tif ( ! isset($this->_modes['mcrypt'][$params['mode']]))\n\t\t\t{\n\t\t\t\tlog_message('error', 'Encryption: MCrypt mode '.strtoupper($params['mode']).' is not available.');\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$this->_mode = $this->_modes['mcrypt'][$params['mode']];\n\t\t\t}\n\t\t}\n\n\t\tif (isset($this->_cipher, $this->_mode))\n\t\t{\n\t\t\tif (is_resource($this->_handle)\n\t\t\t\t&& (strtolower(mcrypt_enc_get_algorithms_name($this->_handle)) !== $this->_cipher\n\t\t\t\t\tOR strtolower(mcrypt_enc_get_modes_name($this->_handle)) !== $this->_mode)\n\t\t\t)\n\t\t\t{\n\t\t\t\tmcrypt_module_close($this->_handle);\n\t\t\t}\n\n\t\t\tif ($this->_handle = mcrypt_module_open($this->_cipher, '', $this->_mode, ''))\n\t\t\t{\n\t\t\t\tlog_message('info', 'Encryption: MCrypt cipher '.strtoupper($this->_cipher).' initialized in '.strtoupper($this->_mode).' mode.');\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tlog_message('error', 'Encryption: Unable to initialize MCrypt with cipher '.strtoupper($this->_cipher).' in '.strtoupper($this->_mode).' mode.');\n\t\t\t}\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Initialize OpenSSL\n\t *\n\t * @param\tarray\t$params\tConfiguration parameters\n\t * @return\tvoid\n\t */\n\tprotected function _openssl_initialize($params)\n\t{\n\t\tif ( ! empty($params['cipher']))\n\t\t{\n\t\t\t$params['cipher'] = strtolower($params['cipher']);\n\t\t\t$this->_cipher_alias($params['cipher']);\n\t\t\t$this->_cipher = $params['cipher'];\n\t\t}\n\n\t\tif ( ! empty($params['mode']))\n\t\t{\n\t\t\t$params['mode'] = strtolower($params['mode']);\n\t\t\tif ( ! isset($this->_modes['openssl'][$params['mode']]))\n\t\t\t{\n\t\t\t\tlog_message('error', 'Encryption: OpenSSL mode '.strtoupper($params['mode']).' is not available.');\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$this->_mode = $this->_modes['openssl'][$params['mode']];\n\t\t\t}\n\t\t}\n\n\t\tif (isset($this->_cipher, $this->_mode))\n\t\t{\n\t\t\t// This is mostly for the stream mode, which doesn't get suffixed in OpenSSL\n\t\t\t$handle = empty($this->_mode)\n\t\t\t\t? $this->_cipher\n\t\t\t\t: $this->_cipher.'-'.$this->_mode;\n\n\t\t\tif ( ! in_array($handle, openssl_get_cipher_methods(), TRUE))\n\t\t\t{\n\t\t\t\t$this->_handle = NULL;\n\t\t\t\tlog_message('error', 'Encryption: Unable to initialize OpenSSL with method '.strtoupper($handle).'.');\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$this->_handle = $handle;\n\t\t\t\tlog_message('info', 'Encryption: OpenSSL initialized with method '.strtoupper($handle).'.');\n\t\t\t}\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Create a random key\n\t *\n\t * @param\tint\t$length\tOutput length\n\t * @return\tstring\n\t */\n\tpublic function create_key($length)\n\t{\n\t\tif (function_exists('random_bytes'))\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\treturn random_bytes((int) $length);\n\t\t\t}\n\t\t\tcatch (Exception $e)\n\t\t\t{\n\t\t\t\tlog_message('error', $e->getMessage());\n\t\t\t\treturn FALSE;\n\t\t\t}\n\t\t}\n\t\telseif (defined('MCRYPT_DEV_URANDOM'))\n\t\t{\n\t\t\treturn mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);\n\t\t}\n\n\t\t$is_secure = NULL;\n\t\t$key = openssl_random_pseudo_bytes($length, $is_secure);\n\t\treturn ($is_secure === TRUE)\n\t\t\t? $key\n\t\t\t: FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Encrypt\n\t *\n\t * @param\tstring\t$data\tInput data\n\t * @param\tarray\t$params\tInput parameters\n\t * @return\tstring\n\t */\n\tpublic function encrypt($data, array $params = NULL)\n\t{\n\t\tif (($params = $this->_get_params($params)) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tisset($params['key']) OR $params['key'] = $this->hkdf($this->_key, 'sha512', NULL, self::strlen($this->_key), 'encryption');\n\n\t\tif (($data = $this->{'_'.$this->_driver.'_encrypt'}($data, $params)) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$params['base64'] && $data = base64_encode($data);\n\n\t\tif (isset($params['hmac_digest']))\n\t\t{\n\t\t\tisset($params['hmac_key']) OR $params['hmac_key'] = $this->hkdf($this->_key, 'sha512', NULL, NULL, 'authentication');\n\t\t\treturn hash_hmac($params['hmac_digest'], $data, $params['hmac_key'], ! $params['base64']).$data;\n\t\t}\n\n\t\treturn $data;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Encrypt via MCrypt\n\t *\n\t * @param\tstring\t$data\tInput data\n\t * @param\tarray\t$params\tInput parameters\n\t * @return\tstring\n\t */\n\tprotected function _mcrypt_encrypt($data, $params)\n\t{\n\t\tif ( ! is_resource($params['handle']))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// The greater-than-1 comparison is mostly a work-around for a bug,\n\t\t// where 1 is returned for ARCFour instead of 0.\n\t\t$iv = (($iv_size = mcrypt_enc_get_iv_size($params['handle'])) > 1)\n\t\t\t? $this->create_key($iv_size)\n\t\t\t: NULL;\n\n\t\tif (mcrypt_generic_init($params['handle'], $params['key'], $iv) < 0)\n\t\t{\n\t\t\tif ($params['handle'] !== $this->_handle)\n\t\t\t{\n\t\t\t\tmcrypt_module_close($params['handle']);\n\t\t\t}\n\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Use PKCS#7 padding in order to ensure compatibility with OpenSSL\n\t\t// and other implementations outside of PHP.\n\t\tif (in_array(strtolower(mcrypt_enc_get_modes_name($params['handle'])), array('cbc', 'ecb'), TRUE))\n\t\t{\n\t\t\t$block_size = mcrypt_enc_get_block_size($params['handle']);\n\t\t\t$pad = $block_size - (self::strlen($data) % $block_size);\n\t\t\t$data .= str_repeat(chr($pad), $pad);\n\t\t}\n\n\t\t// Work-around for yet another strange behavior in MCrypt.\n\t\t//\n\t\t// When encrypting in ECB mode, the IV is ignored. Yet\n\t\t// mcrypt_enc_get_iv_size() returns a value larger than 0\n\t\t// even if ECB is used AND mcrypt_generic_init() complains\n\t\t// if you don't pass an IV with length equal to the said\n\t\t// return value.\n\t\t//\n\t\t// This probably would've been fine (even though still wasteful),\n\t\t// but OpenSSL isn't that dumb and we need to make the process\n\t\t// portable, so ...\n\t\t$data = (mcrypt_enc_get_modes_name($params['handle']) !== 'ECB')\n\t\t\t? $iv.mcrypt_generic($params['handle'], $data)\n\t\t\t: mcrypt_generic($params['handle'], $data);\n\n\t\tmcrypt_generic_deinit($params['handle']);\n\t\tif ($params['handle'] !== $this->_handle)\n\t\t{\n\t\t\tmcrypt_module_close($params['handle']);\n\t\t}\n\n\t\treturn $data;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Encrypt via OpenSSL\n\t *\n\t * @param\tstring\t$data\tInput data\n\t * @param\tarray\t$params\tInput parameters\n\t * @return\tstring\n\t */\n\tprotected function _openssl_encrypt($data, $params)\n\t{\n\t\tif (empty($params['handle']))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$iv = ($iv_size = openssl_cipher_iv_length($params['handle']))\n\t\t\t? $this->create_key($iv_size)\n\t\t\t: '';\n\n\t\t$data = openssl_encrypt(\n\t\t\t$data,\n\t\t\t$params['handle'],\n\t\t\t$params['key'],\n\t\t\tOPENSSL_RAW_DATA,\n\t\t\t$iv\n\t\t);\n\n\t\tif ($data === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn $iv.$data;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Decrypt\n\t *\n\t * @param\tstring\t$data\tEncrypted data\n\t * @param\tarray\t$params\tInput parameters\n\t * @return\tstring\n\t */\n\tpublic function decrypt($data, array $params = NULL)\n\t{\n\t\tif (($params = $this->_get_params($params)) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif (isset($params['hmac_digest']))\n\t\t{\n\t\t\t// This might look illogical, but it is done during encryption as well ...\n\t\t\t// The 'base64' value is effectively an inverted \"raw data\" parameter\n\t\t\t$digest_size = ($params['base64'])\n\t\t\t\t? $this->_digests[$params['hmac_digest']] * 2\n\t\t\t\t: $this->_digests[$params['hmac_digest']];\n\n\t\t\tif (self::strlen($data) <= $digest_size)\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\t$hmac_input = self::substr($data, 0, $digest_size);\n\t\t\t$data = self::substr($data, $digest_size);\n\n\t\t\tisset($params['hmac_key']) OR $params['hmac_key'] = $this->hkdf($this->_key, 'sha512', NULL, NULL, 'authentication');\n\t\t\t$hmac_check = hash_hmac($params['hmac_digest'], $data, $params['hmac_key'], ! $params['base64']);\n\n\t\t\t// Time-attack-safe comparison\n\t\t\t$diff = 0;\n\t\t\tfor ($i = 0; $i < $digest_size; $i++)\n\t\t\t{\n\t\t\t\t$diff |= ord($hmac_input[$i]) ^ ord($hmac_check[$i]);\n\t\t\t}\n\n\t\t\tif ($diff !== 0)\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\t\t}\n\n\t\tif ($params['base64'])\n\t\t{\n\t\t\t$data = base64_decode($data);\n\t\t}\n\n\t\tisset($params['key']) OR $params['key'] = $this->hkdf($this->_key, 'sha512', NULL, self::strlen($this->_key), 'encryption');\n\n\t\treturn $this->{'_'.$this->_driver.'_decrypt'}($data, $params);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Decrypt via MCrypt\n\t *\n\t * @param\tstring\t$data\tEncrypted data\n\t * @param\tarray\t$params\tInput parameters\n\t * @return\tstring\n\t */\n\tprotected function _mcrypt_decrypt($data, $params)\n\t{\n\t\tif ( ! is_resource($params['handle']))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// The greater-than-1 comparison is mostly a work-around for a bug,\n\t\t// where 1 is returned for ARCFour instead of 0.\n\t\tif (($iv_size = mcrypt_enc_get_iv_size($params['handle'])) > 1)\n\t\t{\n\t\t\tif (mcrypt_enc_get_modes_name($params['handle']) !== 'ECB')\n\t\t\t{\n\t\t\t\t$iv = self::substr($data, 0, $iv_size);\n\t\t\t\t$data = self::substr($data, $iv_size);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// MCrypt is dumb and this is ignored, only size matters\n\t\t\t\t$iv = str_repeat(\"\\x0\", $iv_size);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$iv = '';\n\t\t}\n\n\t\tif (mcrypt_generic_init($params['handle'], $params['key'], $iv) < 0)\n\t\t{\n\t\t\tif ($params['handle'] !== $this->_handle)\n\t\t\t{\n\t\t\t\tmcrypt_module_close($params['handle']);\n\t\t\t}\n\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$data = mdecrypt_generic($params['handle'], $data);\n\t\t// Remove PKCS#7 padding, if necessary\n\t\tif (in_array(strtolower(mcrypt_enc_get_modes_name($params['handle'])), array('cbc', 'ecb'), TRUE))\n\t\t{\n\t\t\t$data = self::substr($data, 0, -ord($data[self::strlen($data)-1]));\n\t\t}\n\n\t\tmcrypt_generic_deinit($params['handle']);\n\t\tif ($params['handle'] !== $this->_handle)\n\t\t{\n\t\t\tmcrypt_module_close($params['handle']);\n\t\t}\n\n\t\treturn $data;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Decrypt via OpenSSL\n\t *\n\t * @param\tstring\t$data\tEncrypted data\n\t * @param\tarray\t$params\tInput parameters\n\t * @return\tstring\n\t */\n\tprotected function _openssl_decrypt($data, $params)\n\t{\n\t\tif ($iv_size = openssl_cipher_iv_length($params['handle']))\n\t\t{\n\t\t\t$iv = self::substr($data, 0, $iv_size);\n\t\t\t$data = self::substr($data, $iv_size);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$iv = '';\n\t\t}\n\n\t\treturn empty($params['handle'])\n\t\t\t? FALSE\n\t\t\t: openssl_decrypt(\n\t\t\t\t$data,\n\t\t\t\t$params['handle'],\n\t\t\t\t$params['key'],\n\t\t\t\tOPENSSL_RAW_DATA,\n\t\t\t\t$iv\n\t\t\t);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get params\n\t *\n\t * @param\tarray\t$params\tInput parameters\n\t * @return\tarray\n\t */\n\tprotected function _get_params($params)\n\t{\n\t\tif (empty($params))\n\t\t{\n\t\t\treturn isset($this->_cipher, $this->_mode, $this->_key, $this->_handle)\n\t\t\t\t? array(\n\t\t\t\t\t'handle' => $this->_handle,\n\t\t\t\t\t'cipher' => $this->_cipher,\n\t\t\t\t\t'mode' => $this->_mode,\n\t\t\t\t\t'key' => NULL,\n\t\t\t\t\t'base64' => TRUE,\n\t\t\t\t\t'hmac_digest' => 'sha512',\n\t\t\t\t\t'hmac_key' => NULL\n\t\t\t\t)\n\t\t\t\t: FALSE;\n\t\t}\n\t\telseif ( ! isset($params['cipher'], $params['mode'], $params['key']))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif (isset($params['mode']))\n\t\t{\n\t\t\t$params['mode'] = strtolower($params['mode']);\n\t\t\tif ( ! isset($this->_modes[$this->_driver][$params['mode']]))\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\t$params['mode'] = $this->_modes[$this->_driver][$params['mode']];\n\t\t}\n\n\t\tif (isset($params['hmac']) && $params['hmac'] === FALSE)\n\t\t{\n\t\t\t$params['hmac_digest'] = $params['hmac_key'] = NULL;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif ( ! isset($params['hmac_key']))\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\t\t\telseif (isset($params['hmac_digest']))\n\t\t\t{\n\t\t\t\t$params['hmac_digest'] = strtolower($params['hmac_digest']);\n\t\t\t\tif ( ! isset($this->_digests[$params['hmac_digest']]))\n\t\t\t\t{\n\t\t\t\t\treturn FALSE;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$params['hmac_digest'] = 'sha512';\n\t\t\t}\n\t\t}\n\n\t\t$params = array(\n\t\t\t'handle' => NULL,\n\t\t\t'cipher' => $params['cipher'],\n\t\t\t'mode' => $params['mode'],\n\t\t\t'key' => $params['key'],\n\t\t\t'base64' => isset($params['raw_data']) ? ! $params['raw_data'] : FALSE,\n\t\t\t'hmac_digest' => $params['hmac_digest'],\n\t\t\t'hmac_key' => $params['hmac_key']\n\t\t);\n\n\t\t$this->_cipher_alias($params['cipher']);\n\t\t$params['handle'] = ($params['cipher'] !== $this->_cipher OR $params['mode'] !== $this->_mode)\n\t\t\t? $this->{'_'.$this->_driver.'_get_handle'}($params['cipher'], $params['mode'])\n\t\t\t: $this->_handle;\n\n\t\treturn $params;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get MCrypt handle\n\t *\n\t * @param\tstring\t$cipher\tCipher name\n\t * @param\tstring\t$mode\tEncryption mode\n\t * @return\tresource\n\t */\n\tprotected function _mcrypt_get_handle($cipher, $mode)\n\t{\n\t\treturn mcrypt_module_open($cipher, '', $mode, '');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get OpenSSL handle\n\t *\n\t * @param\tstring\t$cipher\tCipher name\n\t * @param\tstring\t$mode\tEncryption mode\n\t * @return\tstring\n\t */\n\tprotected function _openssl_get_handle($cipher, $mode)\n\t{\n\t\t// OpenSSL methods aren't suffixed with '-stream' for this mode\n\t\treturn ($mode === 'stream')\n\t\t\t? $cipher\n\t\t\t: $cipher.'-'.$mode;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Cipher alias\n\t *\n\t * Tries to translate cipher names between MCrypt and OpenSSL's \"dialects\".\n\t *\n\t * @param\tstring\t$cipher\tCipher name\n\t * @return\tvoid\n\t */\n\tprotected function _cipher_alias(&$cipher)\n\t{\n\t\tstatic $dictionary;\n\n\t\tif (empty($dictionary))\n\t\t{\n\t\t\t$dictionary = array(\n\t\t\t\t'mcrypt' => array(\n\t\t\t\t\t'aes-128' => 'rijndael-128',\n\t\t\t\t\t'aes-192' => 'rijndael-128',\n\t\t\t\t\t'aes-256' => 'rijndael-128',\n\t\t\t\t\t'des3-ede3' => 'tripledes',\n\t\t\t\t\t'bf' => 'blowfish',\n\t\t\t\t\t'cast5' => 'cast-128',\n\t\t\t\t\t'rc4' => 'arcfour',\n\t\t\t\t\t'rc4-40' => 'arcfour'\n\t\t\t\t),\n\t\t\t\t'openssl' => array(\n\t\t\t\t\t'rijndael-128' => 'aes-128',\n\t\t\t\t\t'tripledes' => 'des-ede3',\n\t\t\t\t\t'blowfish' => 'bf',\n\t\t\t\t\t'cast-128' => 'cast5',\n\t\t\t\t\t'arcfour' => 'rc4-40',\n\t\t\t\t\t'rc4' => 'rc4-40'\n\t\t\t\t)\n\t\t\t);\n\n\t\t\t// Notes:\n\t\t\t//\n\t\t\t// - Rijndael-128 is, at the same time all three of AES-128,\n\t\t\t//   AES-192 and AES-256. The only difference between them is\n\t\t\t//   the key size. Rijndael-192, Rijndael-256 on the other hand\n\t\t\t//   also have different block sizes and are NOT AES-compatible.\n\t\t\t//\n\t\t\t// - Blowfish is said to be supporting key sizes between\n\t\t\t//   4 and 56 bytes, but it appears that between MCrypt and\n\t\t\t//   OpenSSL, only those of 16 and more bytes are compatible.\n\t\t\t//   Also, don't know what MCrypt's 'blowfish-compat' is.\n\t\t\t//\n\t\t\t// - CAST-128/CAST5 produces a longer cipher when encrypted via\n\t\t\t//   OpenSSL, but (strangely enough) can be decrypted by either\n\t\t\t//   extension anyway.\n\t\t\t//   Also, it appears that OpenSSL uses 16 rounds regardless of\n\t\t\t//   the key size, while RFC2144 says that for key sizes lower\n\t\t\t//   than 11 bytes, only 12 rounds should be used. This makes\n\t\t\t//   it portable only with keys of between 11 and 16 bytes.\n\t\t\t//\n\t\t\t// - RC4 (ARCFour) has a strange implementation under OpenSSL.\n\t\t\t//   Its 'rc4-40' cipher method seems to work flawlessly, yet\n\t\t\t//   there's another one, 'rc4' that only works with a 16-byte key.\n\t\t\t//\n\t\t\t// - DES is compatible, but doesn't need an alias.\n\t\t\t//\n\t\t\t// Other seemingly matching ciphers between MCrypt, OpenSSL:\n\t\t\t//\n\t\t\t// - RC2 is NOT compatible and only an obscure forum post\n\t\t\t//   confirms that it is MCrypt's fault.\n\t\t}\n\n\t\tif (isset($dictionary[$this->_driver][$cipher]))\n\t\t{\n\t\t\t$cipher = $dictionary[$this->_driver][$cipher];\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * HKDF\n\t *\n\t * @link\thttps://tools.ietf.org/rfc/rfc5869.txt\n\t * @param\t$key\tInput key\n\t * @param\t$digest\tA SHA-2 hashing algorithm\n\t * @param\t$salt\tOptional salt\n\t * @param\t$length\tOutput length (defaults to the selected digest size)\n\t * @param\t$info\tOptional context/application-specific info\n\t * @return\tstring\tA pseudo-random key\n\t */\n\tpublic function hkdf($key, $digest = 'sha512', $salt = NULL, $length = NULL, $info = '')\n\t{\n\t\tif ( ! isset($this->_digests[$digest]))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif (empty($length) OR ! is_int($length))\n\t\t{\n\t\t\t$length = $this->_digests[$digest];\n\t\t}\n\t\telseif ($length > (255 * $this->_digests[$digest]))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tself::strlen($salt) OR $salt = str_repeat(\"\\0\", $this->_digests[$digest]);\n\n\t\t$prk = hash_hmac($digest, $key, $salt, TRUE);\n\t\t$key = '';\n\t\tfor ($key_block = '', $block_index = 1; self::strlen($key) < $length; $block_index++)\n\t\t{\n\t\t\t$key_block = hash_hmac($digest, $key_block.$info.chr($block_index), $prk, TRUE);\n\t\t\t$key .= $key_block;\n\t\t}\n\n\t\treturn self::substr($key, 0, $length);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * __get() magic\n\t *\n\t * @param\tstring\t$key\tProperty name\n\t * @return\tmixed\n\t */\n\tpublic function __get($key)\n\t{\n\t\t// Because aliases\n\t\tif ($key === 'mode')\n\t\t{\n\t\t\treturn array_search($this->_mode, $this->_modes[$this->_driver], TRUE);\n\t\t}\n\t\telseif (in_array($key, array('cipher', 'driver', 'drivers', 'digests'), TRUE))\n\t\t{\n\t\t\treturn $this->{'_'.$key};\n\t\t}\n\n\t\treturn NULL;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Byte-safe strlen()\n\t *\n\t * @param\tstring\t$str\n\t * @return\tint\n\t */\n\tprotected static function strlen($str)\n\t{\n\t\treturn (self::$func_overload)\n\t\t\t? mb_strlen((string) $str, '8bit')\n\t\t\t: strlen((string) $str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Byte-safe substr()\n\t *\n\t * @param\tstring\t$str\n\t * @param\tint\t$start\n\t * @param\tint\t$length\n\t * @return\tstring\n\t */\n\tprotected static function substr($str, $start, $length = NULL)\n\t{\n\t\tif (self::$func_overload)\n\t\t{\n\t\t\treturn mb_substr($str, $start, $length, '8bit');\n\t\t}\n\n\t\treturn isset($length)\n\t\t\t? substr($str, $start, $length)\n\t\t\t: substr($str, $start);\n\t}\n}\n"
  },
  {
    "path": "system/libraries/Form_validation.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Form Validation Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tValidation\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/form_validation.html\n */\nclass CI_Form_validation {\n\n\t/**\n\t * Reference to the CodeIgniter instance\n\t *\n\t * @var object\n\t */\n\tprotected $CI;\n\n\t/**\n\t * Validation data for the current form submission\n\t *\n\t * @var array\n\t */\n\tprotected $_field_data\t\t= array();\n\n\t/**\n\t * Validation rules for the current form\n\t *\n\t * @var array\n\t */\n\tprotected $_config_rules\t= array();\n\n\t/**\n\t * Array of validation errors\n\t *\n\t * @var array\n\t */\n\tprotected $_error_array\t\t= array();\n\n\t/**\n\t * Array of custom error messages\n\t *\n\t * @var array\n\t */\n\tprotected $_error_messages\t= array();\n\n\t/**\n\t * Start tag for error wrapping\n\t *\n\t * @var string\n\t */\n\tprotected $_error_prefix\t= '<p>';\n\n\t/**\n\t * End tag for error wrapping\n\t *\n\t * @var string\n\t */\n\tprotected $_error_suffix\t= '</p>';\n\n\t/**\n\t * Custom error message\n\t *\n\t * @var string\n\t */\n\tprotected $error_string\t\t= '';\n\n\t/**\n\t * Custom data to validate\n\t *\n\t * @var array\n\t */\n\tpublic $validation_data\t= array();\n\n\t/**\n\t * Initialize Form_Validation class\n\t *\n\t * @param\tarray\t$rules\n\t * @return\tvoid\n\t */\n\tpublic function __construct($rules = array())\n\t{\n\t\t$this->CI =& get_instance();\n\n\t\t// applies delimiters set in config file.\n\t\tif (isset($rules['error_prefix']))\n\t\t{\n\t\t\t$this->_error_prefix = $rules['error_prefix'];\n\t\t\tunset($rules['error_prefix']);\n\t\t}\n\t\tif (isset($rules['error_suffix']))\n\t\t{\n\t\t\t$this->_error_suffix = $rules['error_suffix'];\n\t\t\tunset($rules['error_suffix']);\n\t\t}\n\n\t\t// Validation rules can be stored in a config file.\n\t\t$this->_config_rules = $rules;\n\n\t\t// Automatically load the form helper\n\t\t$this->CI->load->helper('form');\n\n\t\tlog_message('info', 'Form Validation Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Rules\n\t *\n\t * This function takes an array of field names and validation\n\t * rules as input, any custom error messages, validates the info,\n\t * and stores it\n\t *\n\t * @param\tmixed\t$field\n\t * @param\tstring\t$label\n\t * @param\tmixed\t$rules\n\t * @param\tarray\t$errors\n\t * @return\tCI_Form_validation\n\t */\n\tpublic function set_rules($field, $label = null, $rules = null, $errors = array())\n\t{\n\t\t// No reason to set rules if we have no POST data\n\t\t// or a validation array has not been specified\n\t\tif ($this->CI->input->method() !== 'post' && empty($this->validation_data))\n\t\t{\n\t\t\treturn $this;\n\t\t}\n\n\t\t// If an array was passed via the first parameter instead of individual string\n\t\t// values we cycle through it and recursively call this function.\n\t\tif (is_array($field))\n\t\t{\n\t\t\tforeach ($field as $row)\n\t\t\t{\n\t\t\t\t// Houston, we have a problem...\n\t\t\t\tif ( ! isset($row['field'], $row['rules']))\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// If the field label wasn't passed we use the field name\n\t\t\t\t$label = isset($row['label']) ? $row['label'] : $row['field'];\n\n\t\t\t\t// Add the custom error message array\n\t\t\t\t$errors = (isset($row['errors']) && is_array($row['errors'])) ? $row['errors'] : array();\n\n\t\t\t\t// Here we go!\n\t\t\t\t$this->set_rules($row['field'], $label, $row['rules'], $errors);\n\t\t\t}\n\n\t\t\treturn $this;\n\t\t}\n\t\telseif ( ! isset($rules))\n\t\t{\n\t\t\tthrow new BadMethodCallException('Form_validation: set_rules() called without a $rules parameter');\n\t\t}\n\n\t\t// No fields or no rules? Nothing to do...\n\t\tif ( ! is_string($field) OR $field === '' OR empty($rules))\n\t\t{\n\t\t\tthrow new RuntimeException('Form_validation: set_rules() called with an empty $rules parameter');\n\t\t}\n\t\telseif ( ! is_array($rules))\n\t\t{\n\t\t\t// BC: Convert pipe-separated rules string to an array\n\t\t\tif ( ! is_string($rules))\n\t\t\t{\n\t\t\t\tthrow new InvalidArgumentException('Form_validation: set_rules() expect $rules to be string or array; '.gettype($rules).' given');\n\t\t\t}\n\n\t\t\t$rules = preg_split('/\\|(?![^\\[]*\\])/', $rules);\n\t\t}\n\n\t\t// If the field label wasn't passed we use the field name\n\t\t$label = ($label === '') ? $field : $label;\n\n\t\t$indexes = array();\n\n\t\t// Is the field name an array? If it is an array, we break it apart\n\t\t// into its components so that we can fetch the corresponding POST data later\n\t\tif (($is_array = (bool) preg_match_all('/\\[(.*?)\\]/', $field, $matches)) === TRUE)\n\t\t{\n\t\t\tsscanf($field, '%[^[][', $indexes[0]);\n\n\t\t\tfor ($i = 0, $c = count($matches[0]); $i < $c; $i++)\n\t\t\t{\n\t\t\t\tif ($matches[1][$i] !== '')\n\t\t\t\t{\n\t\t\t\t\t$indexes[] = $matches[1][$i];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Build our master array\n\t\t$this->_field_data[$field] = array(\n\t\t\t'field'\t\t=> $field,\n\t\t\t'label'\t\t=> $label,\n\t\t\t'rules'\t\t=> $rules,\n\t\t\t'errors'\t=> $errors,\n\t\t\t'is_array'\t=> $is_array,\n\t\t\t'keys'\t\t=> $indexes,\n\t\t\t'postdata'\t=> NULL,\n\t\t\t'error'\t\t=> ''\n\t\t);\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * By default, form validation uses the $_POST array to validate\n\t *\n\t * If an array is set through this method, then this array will\n\t * be used instead of the $_POST array\n\t *\n\t * Note that if you are validating multiple arrays, then the\n\t * reset_validation() function should be called after validating\n\t * each array due to the limitations of CI's singleton\n\t *\n\t * @param\tarray\t$data\n\t * @return\tCI_Form_validation\n\t */\n\tpublic function set_data(array $data)\n\t{\n\t\tif ( ! empty($data))\n\t\t{\n\t\t\t$this->validation_data = $data;\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Error Message\n\t *\n\t * Lets users set their own error messages on the fly. Note:\n\t * The key name has to match the function name that it corresponds to.\n\t *\n\t * @param\tarray\n\t * @param\tstring\n\t * @return\tCI_Form_validation\n\t */\n\tpublic function set_message($lang, $val = '')\n\t{\n\t\tif ( ! is_array($lang))\n\t\t{\n\t\t\t$lang = array($lang => $val);\n\t\t}\n\n\t\t$this->_error_messages = array_merge($this->_error_messages, $lang);\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set The Error Delimiter\n\t *\n\t * Permits a prefix/suffix to be added to each error message\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tCI_Form_validation\n\t */\n\tpublic function set_error_delimiters($prefix = '<p>', $suffix = '</p>')\n\t{\n\t\t$this->_error_prefix = $prefix;\n\t\t$this->_error_suffix = $suffix;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get Error Message\n\t *\n\t * Gets the error message associated with a particular field\n\t *\n\t * @param\tstring\t$field\tField name\n\t * @param\tstring\t$prefix\tHTML start tag\n\t * @param \tstring\t$suffix\tHTML end tag\n\t * @return\tstring\n\t */\n\tpublic function error($field, $prefix = '', $suffix = '')\n\t{\n\t\tif (empty($this->_field_data[$field]['error']))\n\t\t{\n\t\t\treturn '';\n\t\t}\n\n\t\tif ($prefix === '')\n\t\t{\n\t\t\t$prefix = $this->_error_prefix;\n\t\t}\n\n\t\tif ($suffix === '')\n\t\t{\n\t\t\t$suffix = $this->_error_suffix;\n\t\t}\n\n\t\treturn $prefix.$this->_field_data[$field]['error'].$suffix;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get Array of Error Messages\n\t *\n\t * Returns the error messages as an array\n\t *\n\t * @return\tarray\n\t */\n\tpublic function error_array()\n\t{\n\t\treturn $this->_error_array;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Error String\n\t *\n\t * Returns the error messages as a string, wrapped in the error delimiters\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tpublic function error_string($prefix = '', $suffix = '')\n\t{\n\t\t// No errors, validation passes!\n\t\tif (count($this->_error_array) === 0)\n\t\t{\n\t\t\treturn '';\n\t\t}\n\n\t\tif ($prefix === '')\n\t\t{\n\t\t\t$prefix = $this->_error_prefix;\n\t\t}\n\n\t\tif ($suffix === '')\n\t\t{\n\t\t\t$suffix = $this->_error_suffix;\n\t\t}\n\n\t\t// Generate the error string\n\t\t$str = '';\n\t\tforeach ($this->_error_array as $val)\n\t\t{\n\t\t\tif ($val !== '')\n\t\t\t{\n\t\t\t\t$str .= $prefix.$val.$suffix.\"\\n\";\n\t\t\t}\n\t\t}\n\n\t\treturn $str;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Run the Validator\n\t *\n\t * This function does all the work.\n\t *\n\t * @param\tstring\t$config\n\t * @param\tarray\t$data\n\t * @return\tbool\n\t */\n\tpublic function run($config = NULL, &$data = NULL)\n\t{\n\t\t$validation_array = empty($this->validation_data)\n\t\t\t? $_POST\n\t\t\t: $this->validation_data;\n\n\t\t// Does the _field_data array containing the validation rules exist?\n\t\t// If not, we look to see if they were assigned via a config file\n\t\tif (count($this->_field_data) === 0)\n\t\t{\n\t\t\t// No validation rules?  We're done...\n\t\t\tif (empty($this->_config_rules))\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\tif (empty($config))\n\t\t\t{\n\t\t\t\t// Is there a validation rule for the particular URI being accessed?\n\t\t\t\t$config = trim($this->CI->uri->ruri_string(), '/');\n\t\t\t\tisset($this->_config_rules[$config]) OR $config = $this->CI->router->class.'/'.$this->CI->router->method;\n\t\t\t}\n\n\t\t\t$this->set_rules(isset($this->_config_rules[$config]) ? $this->_config_rules[$config] : $this->_config_rules);\n\n\t\t\t// Were we able to set the rules correctly?\n\t\t\tif (count($this->_field_data) === 0)\n\t\t\t{\n\t\t\t\tlog_message('debug', 'Unable to find validation rules');\n\t\t\t\treturn FALSE;\n\t\t\t}\n\t\t}\n\n\t\t// Load the language file containing error messages\n\t\t$this->CI->lang->load('form_validation');\n\n\t\t// Cycle through the rules for each field and match the corresponding $validation_data item\n\t\tforeach ($this->_field_data as $field => &$row)\n\t\t{\n\t\t\t// Fetch the data from the validation_data array item and cache it in the _field_data array.\n\t\t\t// Depending on whether the field name is an array or a string will determine where we get it from.\n\t\t\tif ($row['is_array'] === TRUE)\n\t\t\t{\n\t\t\t\t$this->_field_data[$field]['postdata'] = $this->_reduce_array($validation_array, $row['keys']);\n\t\t\t}\n\t\t\telseif (isset($validation_array[$field]))\n\t\t\t{\n\t\t\t\t$this->_field_data[$field]['postdata'] = $validation_array[$field];\n\t\t\t}\n\t\t}\n\n\t\t// Execute validation rules\n\t\t// Note: A second foreach (for now) is required in order to avoid false-positives\n\t\t//\t for rules like 'matches', which correlate to other validation fields.\n\t\tforeach ($this->_field_data as $field => &$row)\n\t\t{\n\t\t\t// Don't try to validate if we have no rules set\n\t\t\tif (empty($row['rules']))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$this->_execute($row, $row['rules'], $row['postdata']);\n\t\t}\n\n\t\tif ( ! empty($this->_error_array))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Fill $data if requested, otherwise modify $_POST, as long as\n\t\t// set_data() wasn't used (yea, I know it sounds confusing)\n\t\tif (func_num_args() >= 2)\n\t\t{\n\t\t\t$data = empty($this->validation_data) ? $_POST : $this->validation_data;\n\t\t\t$this->_reset_data_array($data);\n\t\t\treturn TRUE;\n\t\t}\n\n\t\tempty($this->validation_data) && $this->_reset_data_array($_POST);\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Prepare rules\n\t *\n\t * Re-orders the provided rules in order of importance, so that\n\t * they can easily be executed later without weird checks ...\n\t *\n\t * \"Callbacks\" are given the highest priority (always called),\n\t * followed by 'required' (called if callbacks didn't fail),\n\t * and then every next rule depends on the previous one passing.\n\t *\n\t * @param\tarray\t$rules\n\t * @return\tarray\n\t */\n\tprotected function _prepare_rules($rules)\n\t{\n\t\t$new_rules = array();\n\t\t$callbacks = array();\n\n\t\tforeach ($rules as &$rule)\n\t\t{\n\t\t\t// Let 'required' always be the first (non-callback) rule\n\t\t\tif ($rule === 'required')\n\t\t\t{\n\t\t\t\tarray_unshift($new_rules, 'required');\n\t\t\t}\n\t\t\t// 'isset' is a kind of a weird alias for 'required' ...\n\t\t\telseif ($rule === 'isset' && (empty($new_rules) OR $new_rules[0] !== 'required'))\n\t\t\t{\n\t\t\t\tarray_unshift($new_rules, 'isset');\n\t\t\t}\n\t\t\t// The old/classic 'callback_'-prefixed rules\n\t\t\telseif (is_string($rule) && strncmp('callback_', $rule, 9) === 0)\n\t\t\t{\n\t\t\t\t$callbacks[] = $rule;\n\t\t\t}\n\t\t\t// Proper callables\n\t\t\telseif (is_callable($rule))\n\t\t\t{\n\t\t\t\t$callbacks[] = $rule;\n\t\t\t}\n\t\t\t// \"Named\" callables; i.e. array('name' => $callable)\n\t\t\telseif (is_array($rule) && isset($rule[0], $rule[1]) && is_callable($rule[1]))\n\t\t\t{\n\t\t\t\t$callbacks[] = $rule;\n\t\t\t}\n\t\t\t// Everything else goes at the end of the queue\n\t\t\telse\n\t\t\t{\n\t\t\t\t$new_rules[] = $rule;\n\t\t\t}\n\t\t}\n\n\t\treturn array_merge($callbacks, $new_rules);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Traverse a multidimensional $_POST array index until the data is found\n\t *\n\t * @param\tarray\n\t * @param\tarray\n\t * @param\tint\n\t * @return\tmixed\n\t */\n\tprotected function _reduce_array($array, $keys, $i = 0)\n\t{\n\t\tif (is_array($array) && isset($keys[$i]))\n\t\t{\n\t\t\treturn isset($array[$keys[$i]]) ? $this->_reduce_array($array[$keys[$i]], $keys, ($i+1)) : NULL;\n\t\t}\n\n\t\t// NULL must be returned for empty fields\n\t\treturn ($array === '') ? NULL : $array;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Re-populate the _POST array with our finalized and processed data\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _reset_data_array(&$data)\n\t{\n\t\tforeach ($this->_field_data as $field => $row)\n\t\t{\n\t\t\tif ($row['postdata'] !== NULL)\n\t\t\t{\n\t\t\t\tif ($row['is_array'] === FALSE)\n\t\t\t\t{\n\t\t\t\t\tisset($data[$field]) && $data[$field] = is_array($row['postdata']) ? NULL : $row['postdata'];\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$data_ref =& $data;\n\n\t\t\t\t\t// before we assign values, make a reference to the right POST key\n\t\t\t\t\tif (count($row['keys']) === 1)\n\t\t\t\t\t{\n\t\t\t\t\t\t$data_ref =& $data[current($row['keys'])];\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tforeach ($row['keys'] as $val)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t$data_ref =& $data_ref[$val];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t$data_ref = $row['postdata'];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Executes the Validation routines\n\t *\n\t * @param\tarray\n\t * @param\tarray\n\t * @param\tmixed\n\t * @param\tint\n\t * @return\tmixed\n\t */\n\tprotected function _execute($row, $rules, $postdata = NULL, $cycles = 0)\n\t{\n\t\t$allow_arrays = in_array('is_array', $rules, TRUE);\n\n\t\t// If the $_POST data is an array we will run a recursive call\n\t\t//\n\t\t// Note: We MUST check if the array is empty or not!\n\t\t//       Otherwise empty arrays will always pass validation.\n\t\tif ($allow_arrays === FALSE && is_array($postdata) && ! empty($postdata))\n\t\t{\n\t\t\tforeach ($postdata as $key => $val)\n\t\t\t{\n\t\t\t\t$this->_execute($row, $rules, $val, $key);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\t$rules = $this->_prepare_rules($rules);\n\t\tforeach ($rules as $rule)\n\t\t{\n\t\t\t$_in_array = FALSE;\n\n\t\t\t// We set the $postdata variable with the current data in our master array so that\n\t\t\t// each cycle of the loop is dealing with the processed data from the last cycle\n\t\t\tif ($row['is_array'] === TRUE && is_array($this->_field_data[$row['field']]['postdata']))\n\t\t\t{\n\t\t\t\t// We shouldn't need this safety, but just in case there isn't an array index\n\t\t\t\t// associated with this cycle we'll bail out\n\t\t\t\tif ( ! isset($this->_field_data[$row['field']]['postdata'][$cycles]))\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t$postdata = $this->_field_data[$row['field']]['postdata'][$cycles];\n\t\t\t\t$_in_array = TRUE;\n\t\t\t}\n\t\t\t// If we get an array field, but it's not expected - then it is most likely\n\t\t\t// somebody messing with the form on the client side, so we'll just consider\n\t\t\t// it an empty field\n\t\t\telseif ($allow_arrays === FALSE && is_array($this->_field_data[$row['field']]['postdata']))\n\t\t\t{\n\t\t\t\t$postdata = NULL;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$postdata = $this->_field_data[$row['field']]['postdata'];\n\t\t\t}\n\n\t\t\t// Is the rule a callback?\n\t\t\t$callback = $callable = FALSE;\n\t\t\tif (is_string($rule))\n\t\t\t{\n\t\t\t\tif (strpos($rule, 'callback_') === 0)\n\t\t\t\t{\n\t\t\t\t\t$rule = substr($rule, 9);\n\t\t\t\t\t$callback = TRUE;\n\t\t\t\t}\n\t\t\t}\n\t\t\telseif (is_callable($rule))\n\t\t\t{\n\t\t\t\t$callable = TRUE;\n\t\t\t}\n\t\t\telseif (is_array($rule) && isset($rule[0], $rule[1]) && is_callable($rule[1]))\n\t\t\t{\n\t\t\t\t// We have a \"named\" callable, so save the name\n\t\t\t\t$callable = $rule[0];\n\t\t\t\t$rule = $rule[1];\n\t\t\t}\n\n\t\t\t// Strip the parameter (if exists) from the rule\n\t\t\t// Rules can contain a parameter: max_length[5]\n\t\t\t$param = FALSE;\n\t\t\tif ( ! $callable && preg_match('/(.*?)\\[(.*)\\]/', $rule, $match))\n\t\t\t{\n\t\t\t\t$rule = $match[1];\n\t\t\t\t$param = $match[2];\n\t\t\t}\n\n\t\t\t// Ignore empty, non-required inputs with a few exceptions ...\n\t\t\tif (\n\t\t\t\t($postdata === NULL OR ($allow_arrays === FALSE && $postdata === ''))\n\t\t\t\t&& $callback === FALSE\n\t\t\t\t&& $callable === FALSE\n\t\t\t\t&& ! in_array($rule, array('required', 'isset', 'matches'), TRUE)\n\t\t\t)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Call the function that corresponds to the rule\n\t\t\tif ($callback OR $callable !== FALSE)\n\t\t\t{\n\t\t\t\tif ($callback)\n\t\t\t\t{\n\t\t\t\t\tif ( ! method_exists($this->CI, $rule))\n\t\t\t\t\t{\n\t\t\t\t\t\tlog_message('debug', 'Unable to find callback validation rule: '.$rule);\n\t\t\t\t\t\t$result = FALSE;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// Run the function and grab the result\n\t\t\t\t\t\t$result = $this->CI->$rule($postdata, $param);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$result = is_array($rule)\n\t\t\t\t\t\t? $rule[0]->{$rule[1]}($postdata)\n\t\t\t\t\t\t: $rule($postdata);\n\n\t\t\t\t\t// Is $callable set to a rule name?\n\t\t\t\t\tif ($callable !== FALSE)\n\t\t\t\t\t{\n\t\t\t\t\t\t$rule = $callable;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Re-assign the result to the master data array\n\t\t\t\tif ($_in_array === TRUE)\n\t\t\t\t{\n\t\t\t\t\t$this->_field_data[$row['field']]['postdata'][$cycles] = is_bool($result) ? $postdata : $result;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$this->_field_data[$row['field']]['postdata'] = is_bool($result) ? $postdata : $result;\n\t\t\t\t}\n\t\t\t}\n\t\t\telseif ( ! method_exists($this, $rule))\n\t\t\t{\n\t\t\t\t// If our own wrapper function doesn't exist we see if a native PHP function does.\n\t\t\t\t// Users can use any native PHP function call that has one param.\n\t\t\t\tif (function_exists($rule))\n\t\t\t\t{\n\t\t\t\t\t// Native PHP functions issue warnings if you pass them more parameters than they use\n\t\t\t\t\t$result = ($param !== FALSE) ? $rule($postdata, $param) : $rule($postdata);\n\n\t\t\t\t\tif ($_in_array === TRUE)\n\t\t\t\t\t{\n\t\t\t\t\t\t$this->_field_data[$row['field']]['postdata'][$cycles] = is_bool($result) ? $postdata : $result;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t$this->_field_data[$row['field']]['postdata'] = is_bool($result) ? $postdata : $result;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tlog_message('debug', 'Unable to find validation rule: '.$rule);\n\t\t\t\t\t$result = FALSE;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$result = $this->$rule($postdata, $param);\n\n\t\t\t\tif ($_in_array === TRUE)\n\t\t\t\t{\n\t\t\t\t\t$this->_field_data[$row['field']]['postdata'][$cycles] = is_bool($result) ? $postdata : $result;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$this->_field_data[$row['field']]['postdata'] = is_bool($result) ? $postdata : $result;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Did the rule test negatively? If so, grab the error.\n\t\t\tif ($result === FALSE)\n\t\t\t{\n\t\t\t\t// Callable rules might not have named error messages\n\t\t\t\tif ( ! is_string($rule))\n\t\t\t\t{\n\t\t\t\t\t$line = $this->CI->lang->line('form_validation_error_message_not_set').'(Anonymous function)';\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$line = $this->_get_error_message($rule, $row['field']);\n\t\t\t\t}\n\n\t\t\t\t// Is the parameter we are inserting into the error message the name\n\t\t\t\t// of another field? If so we need to grab its \"field label\"\n\t\t\t\tif (isset($this->_field_data[$param], $this->_field_data[$param]['label']))\n\t\t\t\t{\n\t\t\t\t\t$param = $this->_translate_fieldname($this->_field_data[$param]['label']);\n\t\t\t\t}\n\n\t\t\t\t// Build the error message\n\t\t\t\t$message = $this->_build_error_msg($line, $this->_translate_fieldname($row['label']), $param);\n\n\t\t\t\t// Save the error message\n\t\t\t\t$this->_field_data[$row['field']]['error'] = $message;\n\n\t\t\t\tif ( ! isset($this->_error_array[$row['field']]))\n\t\t\t\t{\n\t\t\t\t\t$this->_error_array[$row['field']] = $message;\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get the error message for the rule\n\t *\n\t * @param \tstring $rule \tThe rule name\n\t * @param \tstring $field\tThe field name\n\t * @return \tstring\n\t */\n\tprotected function _get_error_message($rule, $field)\n\t{\n\t\t// check if a custom message is defined through validation config row.\n\t\tif (isset($this->_field_data[$field]['errors'][$rule]))\n\t\t{\n\t\t\treturn $this->_field_data[$field]['errors'][$rule];\n\t\t}\n\t\t// check if a custom message has been set using the set_message() function\n\t\telseif (isset($this->_error_messages[$rule]))\n\t\t{\n\t\t\treturn $this->_error_messages[$rule];\n\t\t}\n\t\telseif (FALSE !== ($line = $this->CI->lang->line('form_validation_'.$rule)))\n\t\t{\n\t\t\treturn $line;\n\t\t}\n\n\t\treturn $this->CI->lang->line('form_validation_error_message_not_set').'('.$rule.')';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Translate a field name\n\t *\n\t * @param\tstring\tthe field name\n\t * @return\tstring\n\t */\n\tprotected function _translate_fieldname($fieldname)\n\t{\n\t\t// Do we need to translate the field name? We look for the prefix 'lang:' to determine this\n\t\t// If we find one, but there's no translation for the string - just return it\n\t\tif (sscanf($fieldname, 'lang:%s', $line) === 1 && FALSE === ($fieldname = $this->CI->lang->line($line, FALSE)))\n\t\t{\n\t\t\treturn $line;\n\t\t}\n\n\t\treturn $fieldname;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Build an error message using the field and param.\n\t *\n\t * @param\tstring\tThe error message line\n\t * @param\tstring\tA field's human name\n\t * @param\tmixed\tA rule's optional parameter\n\t * @return\tstring\n\t */\n\tprotected function _build_error_msg($line, $field = '', $param = '')\n\t{\n\t\t// Check for %s in the string for legacy support.\n\t\tif (strpos($line, '%s') !== FALSE)\n\t\t{\n\t\t\treturn sprintf($line, $field, $param);\n\t\t}\n\n\t\treturn str_replace(array('{field}', '{param}'), array($field, $param), $line);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Checks if the rule is present within the validator\n\t *\n\t * Permits you to check if a rule is present within the validator\n\t *\n\t * @param\tstring\tthe field name\n\t * @return\tbool\n\t */\n\tpublic function has_rule($field)\n\t{\n\t\treturn isset($this->_field_data[$field]);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get the value from a form\n\t *\n\t * Permits you to repopulate a form field with the value it was submitted\n\t * with, or, if that value doesn't exist, with the default\n\t *\n\t * @param\tstring\tthe field name\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tpublic function set_value($field = '', $default = '')\n\t{\n\t\tif ( ! isset($this->_field_data[$field], $this->_field_data[$field]['postdata']))\n\t\t{\n\t\t\treturn $default;\n\t\t}\n\n\t\t// If the data is an array output them one at a time.\n\t\t//\tE.g: form_input('name[]', set_value('name[]');\n\t\tif (is_array($this->_field_data[$field]['postdata']))\n\t\t{\n\t\t\treturn array_shift($this->_field_data[$field]['postdata']);\n\t\t}\n\n\t\treturn $this->_field_data[$field]['postdata'];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Select\n\t *\n\t * Enables pull-down lists to be set to the value the user\n\t * selected in the event of an error\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @param\tbool\n\t * @return\tstring\n\t */\n\tpublic function set_select($field = '', $value = '', $default = FALSE)\n\t{\n\t\tif ( ! isset($this->_field_data[$field], $this->_field_data[$field]['postdata']))\n\t\t{\n\t\t\treturn ($default === TRUE && count($this->_field_data) === 0) ? ' selected=\"selected\"' : '';\n\t\t}\n\n\t\t$field = $this->_field_data[$field]['postdata'];\n\t\t$value = (string) $value;\n\t\tif (is_array($field))\n\t\t{\n\t\t\t// Note: in_array('', array(0)) returns TRUE, do not use it\n\t\t\tforeach ($field as &$v)\n\t\t\t{\n\t\t\t\tif ($value === $v)\n\t\t\t\t{\n\t\t\t\t\treturn ' selected=\"selected\"';\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn '';\n\t\t}\n\t\telseif (($field === '' OR $value === '') OR ($field !== $value))\n\t\t{\n\t\t\treturn '';\n\t\t}\n\n\t\treturn ' selected=\"selected\"';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Radio\n\t *\n\t * Enables radio buttons to be set to the value the user\n\t * selected in the event of an error\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @param\tbool\n\t * @return\tstring\n\t */\n\tpublic function set_radio($field = '', $value = '', $default = FALSE)\n\t{\n\t\tif ( ! isset($this->_field_data[$field], $this->_field_data[$field]['postdata']))\n\t\t{\n\t\t\treturn ($default === TRUE && count($this->_field_data) === 0) ? ' checked=\"checked\"' : '';\n\t\t}\n\n\t\t$field = $this->_field_data[$field]['postdata'];\n\t\t$value = (string) $value;\n\t\tif (is_array($field))\n\t\t{\n\t\t\t// Note: in_array('', array(0)) returns TRUE, do not use it\n\t\t\tforeach ($field as &$v)\n\t\t\t{\n\t\t\t\tif ($value === $v)\n\t\t\t\t{\n\t\t\t\t\treturn ' checked=\"checked\"';\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn '';\n\t\t}\n\t\telseif (($field === '' OR $value === '') OR ($field !== $value))\n\t\t{\n\t\t\treturn '';\n\t\t}\n\n\t\treturn ' checked=\"checked\"';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Checkbox\n\t *\n\t * Enables checkboxes to be set to the value the user\n\t * selected in the event of an error\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @param\tbool\n\t * @return\tstring\n\t */\n\tpublic function set_checkbox($field = '', $value = '', $default = FALSE)\n\t{\n\t\t// Logic is exactly the same as for radio fields\n\t\treturn $this->set_radio($field, $value, $default);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Required\n\t *\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function required($str)\n\t{\n\t\treturn is_array($str)\n\t\t\t? (empty($str) === FALSE)\n\t\t\t: (trim((string) $str) !== '');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Performs a Regular Expression match test.\n\t *\n\t * @param\tstring\n\t * @param\tstring\tregex\n\t * @return\tbool\n\t */\n\tpublic function regex_match($str, $regex)\n\t{\n\t\treturn (bool) preg_match($regex, $str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Match one field to another\n\t *\n\t * @param\tstring\t$str\tstring to compare against\n\t * @param\tstring\t$field\n\t * @return\tbool\n\t */\n\tpublic function matches($str, $field)\n\t{\n\t\treturn isset($this->_field_data[$field], $this->_field_data[$field]['postdata'])\n\t\t\t? ($str === $this->_field_data[$field]['postdata'])\n\t\t\t: FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Differs from another field\n\t *\n\t * @param\tstring\n\t * @param\tstring\tfield\n\t * @return\tbool\n\t */\n\tpublic function differs($str, $field)\n\t{\n\t\treturn ! (isset($this->_field_data[$field]) && $this->_field_data[$field]['postdata'] === $str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Is Unique\n\t *\n\t * Check if the input value doesn't already exist\n\t * in the specified database field.\n\t *\n\t * @param\tstring\t$str\n\t * @param\tstring\t$field\n\t * @return\tbool\n\t */\n\tpublic function is_unique($str, $field)\n\t{\n\t\tsscanf($field, '%[^.].%[^.]', $table, $field);\n\t\treturn isset($this->CI->db)\n\t\t\t? ($this->CI->db->limit(1)->get_where($table, array($field => $str))->num_rows() === 0)\n\t\t\t: FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Minimum Length\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function min_length($str, $val)\n\t{\n\t\tif ( ! is_numeric($val))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn ($val <= mb_strlen($str));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Max Length\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function max_length($str, $val)\n\t{\n\t\tif ( ! is_numeric($val))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn ($val >= mb_strlen($str));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Exact Length\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function exact_length($str, $val)\n\t{\n\t\tif ( ! is_numeric($val))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn (mb_strlen($str) === (int) $val);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Valid URL\n\t *\n\t * @param\tstring\t$str\n\t * @return\tbool\n\t */\n\tpublic function valid_url($str)\n\t{\n\t\tif (empty($str))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\t\telseif (preg_match('/^(?:([^:]*)\\:)?\\/\\/(.+)$/', $str, $matches))\n\t\t{\n\t\t\tif (empty($matches[2]))\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\t\t\telseif ( ! in_array(strtolower($matches[1]), array('http', 'https'), TRUE))\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\t$str = $matches[2];\n\t\t}\n\n\t\t// Apparently, FILTER_VALIDATE_URL doesn't reject digit-only names for some reason ...\n\t\t// See https://github.com/bcit-ci/CodeIgniter/issues/5755\n\t\tif (ctype_digit($str))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// PHP 7 accepts IPv6 addresses within square brackets as hostnames,\n\t\t// but it appears that the PR that came in with https://bugs.php.net/bug.php?id=68039\n\t\t// was never merged into a PHP 5 branch ... https://3v4l.org/8PsSN\n\t\tif (preg_match('/^\\[([^\\]]+)\\]/', $str, $matches) && ! is_php('7') && filter_var($matches[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== FALSE)\n\t\t{\n\t\t\t$str = 'ipv6.host'.substr($str, strlen($matches[1]) + 2);\n\t\t}\n\n\t\treturn (filter_var('http://'.$str, FILTER_VALIDATE_URL) !== FALSE);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Valid Email\n\t *\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function valid_email($str)\n\t{\n\t\tif (function_exists('idn_to_ascii') && preg_match('#\\A([^@]+)@(.+)\\z#', $str, $matches))\n\t\t{\n\t\t\t$domain = defined('INTL_IDNA_VARIANT_UTS46')\n\t\t\t\t? idn_to_ascii($matches[2], 0, INTL_IDNA_VARIANT_UTS46)\n\t\t\t\t: idn_to_ascii($matches[2]);\n\n\t\t\tif ($domain !== FALSE)\n\t\t\t{\n\t\t\t\t$str = $matches[1].'@'.$domain;\n\t\t\t}\n\t\t}\n\n\t\treturn (bool) filter_var($str, FILTER_VALIDATE_EMAIL);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Valid Emails\n\t *\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function valid_emails($str)\n\t{\n\t\tif (strpos($str, ',') === FALSE)\n\t\t{\n\t\t\treturn $this->valid_email(trim($str));\n\t\t}\n\n\t\tforeach (explode(',', $str) as $email)\n\t\t{\n\t\t\tif (trim($email) !== '' && $this->valid_email(trim($email)) === FALSE)\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Validate IP Address\n\t *\n\t * @param\tstring\n\t * @param\tstring\t'ipv4' or 'ipv6' to validate a specific IP format\n\t * @return\tbool\n\t */\n\tpublic function valid_ip($ip, $which = '')\n\t{\n\t\treturn $this->CI->input->valid_ip($ip, $which);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Validate MAC address\n\t *\n\t * @param\tstring\t$mac\n\t * @return\tbool\n\t */\n\tpublic function valid_mac($mac)\n\t{\n\t\tif ( ! is_php('5.5'))\n\t\t{\n\t\t\t// Most common format, with either dash or colon delimiters\n\t\t\tif (preg_match('#\\A[0-9a-f]{2}(?<delimiter>[:-])([0-9a-f]{2}(?P=delimiter)){4}[0-9a-f]{2}\\z#i', $mac))\n\t\t\t{\n\t\t\t\treturn TRUE;\n\t\t\t}\n\n\t\t\t// The less common format; e.g. 0123.4567.89ab\n\t\t\treturn (bool) preg_match('#((\\A|\\.)[0-9a-f]{4}){3}\\z#i', $mac);\n\t\t}\n\n\t\treturn (bool) filter_var($mac, FILTER_VALIDATE_MAC);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Alpha\n\t *\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function alpha($str)\n\t{\n\t\treturn ctype_alpha($str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Alpha-numeric\n\t *\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function alpha_numeric($str)\n\t{\n\t\treturn ctype_alnum((string) $str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Alpha-numeric w/ spaces\n\t *\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function alpha_numeric_spaces($str)\n\t{\n\t\treturn (bool) preg_match('/^[A-Z0-9 ]+$/i', $str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Alpha-numeric with underscores and dashes\n\t *\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function alpha_dash($str)\n\t{\n\t\treturn (bool) preg_match('/^[a-z0-9_-]+$/i', $str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Numeric\n\t *\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function numeric($str)\n\t{\n\t\treturn (bool) preg_match('/^[\\-+]?[0-9]*\\.?[0-9]+$/', $str);\n\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Integer\n\t *\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function integer($str)\n\t{\n\t\treturn (bool) preg_match('/^[\\-+]?[0-9]+$/', $str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Decimal number\n\t *\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function decimal($str)\n\t{\n\t\treturn (bool) preg_match('/^[\\-+]?[0-9]+\\.[0-9]+$/', $str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Greater than\n\t *\n\t * @param\tstring\n\t * @param\tint\n\t * @return\tbool\n\t */\n\tpublic function greater_than($str, $min)\n\t{\n\t\treturn is_numeric($str) ? ($str > $min) : FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Equal to or Greater than\n\t *\n\t * @param\tstring\n\t * @param\tint\n\t * @return\tbool\n\t */\n\tpublic function greater_than_equal_to($str, $min)\n\t{\n\t\treturn is_numeric($str) ? ($str >= $min) : FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Less than\n\t *\n\t * @param\tstring\n\t * @param\tint\n\t * @return\tbool\n\t */\n\tpublic function less_than($str, $max)\n\t{\n\t\treturn is_numeric($str) ? ($str < $max) : FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Equal to or Less than\n\t *\n\t * @param\tstring\n\t * @param\tint\n\t * @return\tbool\n\t */\n\tpublic function less_than_equal_to($str, $max)\n\t{\n\t\treturn is_numeric($str) ? ($str <= $max) : FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Value should be within an array of values\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function in_list($value, $list)\n\t{\n\t\treturn in_array($value, explode(',', $list), TRUE);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Is a Natural number  (0,1,2,3, etc.)\n\t *\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function is_natural($str)\n\t{\n\t\treturn ctype_digit((string) $str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Is a Natural number, but not a zero  (1,2,3, etc.)\n\t *\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function is_natural_no_zero($str)\n\t{\n\t\treturn ($str != 0 && ctype_digit((string) $str));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Valid Base64\n\t *\n\t * Tests a string for characters outside of the Base64 alphabet\n\t * as defined by RFC 2045 http://www.faqs.org/rfcs/rfc2045\n\t *\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function valid_base64($str)\n\t{\n\t\treturn (base64_encode(base64_decode($str)) === $str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Prep URL\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tpublic function prep_url($str = '')\n\t{\n\t\tif ($str !== '' && stripos($str, 'http://') !== 0 && stripos($str, 'https://') !== 0)\n\t\t{\n\t\t\treturn 'http://'.$str;\n\t\t}\n\n\t\treturn $str;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Strip Image Tags\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tpublic function strip_image_tags($str)\n\t{\n\t\treturn $this->CI->security->strip_image_tags($str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Convert PHP tags to entities\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tpublic function encode_php_tags($str)\n\t{\n\t\treturn str_replace(array('<?', '?>'), array('&lt;?', '?&gt;'), $str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Reset validation vars\n\t *\n\t * Prevents subsequent validation routines from being affected by the\n\t * results of any previous validation routine due to the CI singleton.\n\t *\n\t * @return\tCI_Form_validation\n\t */\n\tpublic function reset_validation()\n\t{\n\t\t$this->_field_data = array();\n\t\t$this->_error_array = array();\n\t\t$this->_error_messages = array();\n\t\t$this->error_string = '';\n\t\treturn $this;\n\t}\n\n}\n"
  },
  {
    "path": "system/libraries/Ftp.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * FTP Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tLibraries\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/ftp.html\n */\nclass CI_FTP {\n\n\t/**\n\t * FTP Server hostname\n\t *\n\t * @var\tstring\n\t */\n\tpublic $hostname = '';\n\n\t/**\n\t * FTP Username\n\t *\n\t * @var\tstring\n\t */\n\tpublic $username = '';\n\n\t/**\n\t * FTP Password\n\t *\n\t * @var\tstring\n\t */\n\tpublic $password = '';\n\n\t/**\n\t * FTP Server port\n\t *\n\t * @var\tint\n\t */\n\tpublic $port = 21;\n\n\t/**\n\t * Passive mode flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $passive = TRUE;\n\n\t/**\n\t * Debug flag\n\t *\n\t * Specifies whether to display error messages.\n\t *\n\t * @var\tbool\n\t */\n\tpublic $debug = FALSE;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Connection ID\n\t *\n\t * @var\tresource\n\t */\n\tprotected $conn_id;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Constructor\n\t *\n\t * @param\tarray\t$config\n\t * @return\tvoid\n\t */\n\tpublic function __construct($config = array())\n\t{\n\t\tempty($config) OR $this->initialize($config);\n\t\tlog_message('info', 'FTP Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Initialize preferences\n\t *\n\t * @param\tarray\t$config\n\t * @return\tvoid\n\t */\n\tpublic function initialize($config = array())\n\t{\n\t\tforeach ($config as $key => $val)\n\t\t{\n\t\t\tif (isset($this->$key))\n\t\t\t{\n\t\t\t\t$this->$key = $val;\n\t\t\t}\n\t\t}\n\n\t\t// Prep the hostname\n\t\t$this->hostname = preg_replace('|.+?://|', '', $this->hostname);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * FTP Connect\n\t *\n\t * @param\tarray\t $config\tConnection values\n\t * @return\tbool\n\t */\n\tpublic function connect($config = array())\n\t{\n\t\tif (count($config) > 0)\n\t\t{\n\t\t\t$this->initialize($config);\n\t\t}\n\n\t\tif (FALSE === ($this->conn_id = @ftp_connect($this->hostname, $this->port)))\n\t\t{\n\t\t\tif ($this->debug === TRUE)\n\t\t\t{\n\t\t\t\t$this->_error('ftp_unable_to_connect');\n\t\t\t}\n\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif ( ! $this->_login())\n\t\t{\n\t\t\tif ($this->debug === TRUE)\n\t\t\t{\n\t\t\t\t$this->_error('ftp_unable_to_login');\n\t\t\t}\n\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Set passive mode if needed\n\t\tif ($this->passive === TRUE)\n\t\t{\n\t\t\tftp_pasv($this->conn_id, TRUE);\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * FTP Login\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _login()\n\t{\n\t\treturn @ftp_login($this->conn_id, $this->username, $this->password);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Validates the connection ID\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _is_conn()\n\t{\n\t\tif ($this->conn_id === FALSE)\n\t\t{\n\t\t\tif ($this->debug === TRUE)\n\t\t\t{\n\t\t\t\t$this->_error('ftp_no_connection');\n\t\t\t}\n\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Change directory\n\t *\n\t * The second parameter lets us momentarily turn off debugging so that\n\t * this function can be used to test for the existence of a folder\n\t * without throwing an error. There's no FTP equivalent to is_dir()\n\t * so we do it by trying to change to a particular directory.\n\t * Internally, this parameter is only used by the \"mirror\" function below.\n\t *\n\t * @param\tstring\t$path\n\t * @param\tbool\t$suppress_debug\n\t * @return\tbool\n\t */\n\tpublic function changedir($path, $suppress_debug = FALSE)\n\t{\n\t\tif ( ! $this->_is_conn())\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$result = @ftp_chdir($this->conn_id, $path);\n\n\t\tif ($result === FALSE)\n\t\t{\n\t\t\tif ($this->debug === TRUE && $suppress_debug === FALSE)\n\t\t\t{\n\t\t\t\t$this->_error('ftp_unable_to_changedir');\n\t\t\t}\n\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Create a directory\n\t *\n\t * @param\tstring\t$path\n\t * @param\tint\t$permissions\n\t * @return\tbool\n\t */\n\tpublic function mkdir($path, $permissions = NULL)\n\t{\n\t\tif ($path === '' OR ! $this->_is_conn())\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$result = @ftp_mkdir($this->conn_id, $path);\n\n\t\tif ($result === FALSE)\n\t\t{\n\t\t\tif ($this->debug === TRUE)\n\t\t\t{\n\t\t\t\t$this->_error('ftp_unable_to_mkdir');\n\t\t\t}\n\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Set file permissions if needed\n\t\tif ($permissions !== NULL)\n\t\t{\n\t\t\t$this->chmod($path, (int) $permissions);\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Upload a file to the server\n\t *\n\t * @param\tstring\t$locpath\n\t * @param\tstring\t$rempath\n\t * @param\tstring\t$mode\n\t * @param\tint\t$permissions\n\t * @return\tbool\n\t */\n\tpublic function upload($locpath, $rempath, $mode = 'auto', $permissions = NULL)\n\t{\n\t\tif ( ! $this->_is_conn())\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif ( ! file_exists($locpath))\n\t\t{\n\t\t\t$this->_error('ftp_no_source_file');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Set the mode if not specified\n\t\tif ($mode === 'auto')\n\t\t{\n\t\t\t// Get the file extension so we can set the upload type\n\t\t\t$ext = $this->_getext($locpath);\n\t\t\t$mode = $this->_settype($ext);\n\t\t}\n\n\t\t$mode = ($mode === 'ascii') ? FTP_ASCII : FTP_BINARY;\n\n\t\t$result = @ftp_put($this->conn_id, $rempath, $locpath, $mode);\n\n\t\tif ($result === FALSE)\n\t\t{\n\t\t\tif ($this->debug === TRUE)\n\t\t\t{\n\t\t\t\t$this->_error('ftp_unable_to_upload');\n\t\t\t}\n\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Set file permissions if needed\n\t\tif ($permissions !== NULL)\n\t\t{\n\t\t\t$this->chmod($rempath, (int) $permissions);\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Download a file from a remote server to the local server\n\t *\n\t * @param\tstring\t$rempath\n\t * @param\tstring\t$locpath\n\t * @param\tstring\t$mode\n\t * @return\tbool\n\t */\n\tpublic function download($rempath, $locpath, $mode = 'auto')\n\t{\n\t\tif ( ! $this->_is_conn())\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Set the mode if not specified\n\t\tif ($mode === 'auto')\n\t\t{\n\t\t\t// Get the file extension so we can set the upload type\n\t\t\t$ext = $this->_getext($rempath);\n\t\t\t$mode = $this->_settype($ext);\n\t\t}\n\n\t\t$mode = ($mode === 'ascii') ? FTP_ASCII : FTP_BINARY;\n\n\t\t$result = @ftp_get($this->conn_id, $locpath, $rempath, $mode);\n\n\t\tif ($result === FALSE)\n\t\t{\n\t\t\tif ($this->debug === TRUE)\n\t\t\t{\n\t\t\t\t$this->_error('ftp_unable_to_download');\n\t\t\t}\n\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Rename (or move) a file\n\t *\n\t * @param\tstring\t$old_file\n\t * @param\tstring\t$new_file\n\t * @param\tbool\t$move\n\t * @return\tbool\n\t */\n\tpublic function rename($old_file, $new_file, $move = FALSE)\n\t{\n\t\tif ( ! $this->_is_conn())\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$result = @ftp_rename($this->conn_id, $old_file, $new_file);\n\n\t\tif ($result === FALSE)\n\t\t{\n\t\t\tif ($this->debug === TRUE)\n\t\t\t{\n\t\t\t\t$this->_error('ftp_unable_to_'.($move === FALSE ? 'rename' : 'move'));\n\t\t\t}\n\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Move a file\n\t *\n\t * @param\tstring\t$old_file\n\t * @param\tstring\t$new_file\n\t * @return\tbool\n\t */\n\tpublic function move($old_file, $new_file)\n\t{\n\t\treturn $this->rename($old_file, $new_file, TRUE);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Rename (or move) a file\n\t *\n\t * @param\tstring\t$filepath\n\t * @return\tbool\n\t */\n\tpublic function delete_file($filepath)\n\t{\n\t\tif ( ! $this->_is_conn())\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$result = @ftp_delete($this->conn_id, $filepath);\n\n\t\tif ($result === FALSE)\n\t\t{\n\t\t\tif ($this->debug === TRUE)\n\t\t\t{\n\t\t\t\t$this->_error('ftp_unable_to_delete');\n\t\t\t}\n\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Delete a folder and recursively delete everything (including sub-folders)\n\t * contained within it.\n\t *\n\t * @param\tstring\t$filepath\n\t * @return\tbool\n\t */\n\tpublic function delete_dir($filepath)\n\t{\n\t\tif ( ! $this->_is_conn())\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Add a trailing slash to the file path if needed\n\t\t$filepath = preg_replace('/(.+?)\\/*$/', '\\\\1/', $filepath);\n\n\t\t$list = $this->list_files($filepath);\n\t\tif ( ! empty($list))\n\t\t{\n\t\t\tfor ($i = 0, $c = count($list); $i < $c; $i++)\n\t\t\t{\n\t\t\t\t// If we can't delete the item it's probably a directory,\n\t\t\t\t// so we'll recursively call delete_dir()\n\t\t\t\tif ( ! preg_match('#/\\.\\.?$#', $list[$i]) && ! @ftp_delete($this->conn_id, $list[$i]))\n\t\t\t\t{\n\t\t\t\t\t$this->delete_dir($filepath.$list[$i]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (@ftp_rmdir($this->conn_id, $filepath) === FALSE)\n\t\t{\n\t\t\tif ($this->debug === TRUE)\n\t\t\t{\n\t\t\t\t$this->_error('ftp_unable_to_delete');\n\t\t\t}\n\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set file permissions\n\t *\n\t * @param\tstring\t$path\tFile path\n\t * @param\tint\t$perm\tPermissions\n\t * @return\tbool\n\t */\n\tpublic function chmod($path, $perm)\n\t{\n\t\tif ( ! $this->_is_conn())\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif (@ftp_chmod($this->conn_id, $perm, $path) === FALSE)\n\t\t{\n\t\t\tif ($this->debug === TRUE)\n\t\t\t{\n\t\t\t\t$this->_error('ftp_unable_to_chmod');\n\t\t\t}\n\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * FTP List files in the specified directory\n\t *\n\t * @param\tstring\t$path\n\t * @return\tarray\n\t */\n\tpublic function list_files($path = '.')\n\t{\n\t\treturn $this->_is_conn()\n\t\t\t? ftp_nlist($this->conn_id, $path)\n\t\t\t: FALSE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Read a directory and recreate it remotely\n\t *\n\t * This function recursively reads a folder and everything it contains\n\t * (including sub-folders) and creates a mirror via FTP based on it.\n\t * Whatever the directory structure of the original file path will be\n\t * recreated on the server.\n\t *\n\t * @param\tstring\t$locpath\tPath to source with trailing slash\n\t * @param\tstring\t$rempath\tPath to destination - include the base folder with trailing slash\n\t * @return\tbool\n\t */\n\tpublic function mirror($locpath, $rempath)\n\t{\n\t\tif ( ! $this->_is_conn())\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Open the local file path\n\t\tif ($fp = @opendir($locpath))\n\t\t{\n\t\t\t// Attempt to open the remote file path and try to create it, if it doesn't exist\n\t\t\tif ( ! $this->changedir($rempath, TRUE) && ( ! $this->mkdir($rempath) OR ! $this->changedir($rempath)))\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\t// Recursively read the local directory\n\t\t\twhile (FALSE !== ($file = readdir($fp)))\n\t\t\t{\n\t\t\t\tif (is_dir($locpath.$file) && $file[0] !== '.')\n\t\t\t\t{\n\t\t\t\t\t$this->mirror($locpath.$file.'/', $rempath.$file.'/');\n\t\t\t\t}\n\t\t\t\telseif ($file[0] !== '.')\n\t\t\t\t{\n\t\t\t\t\t// Get the file extension so we can se the upload type\n\t\t\t\t\t$ext = $this->_getext($file);\n\t\t\t\t\t$mode = $this->_settype($ext);\n\n\t\t\t\t\t$this->upload($locpath.$file, $rempath.$file, $mode);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Extract the file extension\n\t *\n\t * @param\tstring\t$filename\n\t * @return\tstring\n\t */\n\tprotected function _getext($filename)\n\t{\n\t\treturn (($dot = strrpos($filename, '.')) === FALSE)\n\t\t\t? 'txt'\n\t\t\t: substr($filename, $dot + 1);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set the upload type\n\t *\n\t * @param\tstring\t$ext\tFilename extension\n\t * @return\tstring\n\t */\n\tprotected function _settype($ext)\n\t{\n\t\treturn in_array($ext, array('txt', 'text', 'php', 'phps', 'php4', 'js', 'css', 'htm', 'html', 'phtml', 'shtml', 'log', 'xml'), TRUE)\n\t\t\t? 'ascii'\n\t\t\t: 'binary';\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Close the connection\n\t *\n\t * @return\tbool\n\t */\n\tpublic function close()\n\t{\n\t\treturn $this->_is_conn()\n\t\t\t? @ftp_close($this->conn_id)\n\t\t\t: FALSE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Display error message\n\t *\n\t * @param\tstring\t$line\n\t * @return\tvoid\n\t */\n\tprotected function _error($line)\n\t{\n\t\t$CI =& get_instance();\n\t\t$CI->lang->load('ftp');\n\t\tshow_error($CI->lang->line($line));\n\t}\n\n}\n"
  },
  {
    "path": "system/libraries/Image_lib.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Image Manipulation class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tImage_lib\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/image_lib.html\n */\nclass CI_Image_lib {\n\n\t/**\n\t * PHP extension/library to use for image manipulation\n\t * Can be: imagemagick, netpbm, gd, gd2\n\t *\n\t * @var string\n\t */\n\tpublic $image_library\t\t= 'gd2';\n\n\t/**\n\t * Path to the graphic library (if applicable)\n\t *\n\t * @var string\n\t */\n\tpublic $library_path\t\t= '';\n\n\t/**\n\t * Whether to send to browser or write to disk\n\t *\n\t * @var bool\n\t */\n\tpublic $dynamic_output\t\t= FALSE;\n\n\t/**\n\t * Path to original image\n\t *\n\t * @var string\n\t */\n\tpublic $source_image\t\t= '';\n\n\t/**\n\t * Path to the modified image\n\t *\n\t * @var string\n\t */\n\tpublic $new_image\t\t= '';\n\n\t/**\n\t * Image width\n\t *\n\t * @var int\n\t */\n\tpublic $width\t\t\t= '';\n\n\t/**\n\t * Image height\n\t *\n\t * @var int\n\t */\n\tpublic $height\t\t\t= '';\n\n\t/**\n\t * Quality percentage of new image\n\t *\n\t * @var int\n\t */\n\tpublic $quality\t\t\t= 90;\n\n\t/**\n\t * Whether to create a thumbnail\n\t *\n\t * @var bool\n\t */\n\tpublic $create_thumb\t\t= FALSE;\n\n\t/**\n\t * String to add to thumbnail version of image\n\t *\n\t * @var string\n\t */\n\tpublic $thumb_marker\t\t= '_thumb';\n\n\t/**\n\t * Whether to maintain aspect ratio when resizing or use hard values\n\t *\n\t * @var bool\n\t */\n\tpublic $maintain_ratio\t\t= TRUE;\n\n\t/**\n\t * auto, height, or width.  Determines what to use as the master dimension\n\t *\n\t * @var string\n\t */\n\tpublic $master_dim\t\t= 'auto';\n\n\t/**\n\t * Angle at to rotate image\n\t *\n\t * @var string\n\t */\n\tpublic $rotation_angle\t\t= '';\n\n\t/**\n\t * X Coordinate for manipulation of the current image\n\t *\n\t * @var int\n\t */\n\tpublic $x_axis\t\t\t= '';\n\n\t/**\n\t * Y Coordinate for manipulation of the current image\n\t *\n\t * @var int\n\t */\n\tpublic $y_axis\t\t\t= '';\n\n\t// --------------------------------------------------------------------------\n\t// Watermark Vars\n\t// --------------------------------------------------------------------------\n\n\t/**\n\t * Watermark text if graphic is not used\n\t *\n\t * @var string\n\t */\n\tpublic $wm_text\t\t\t= '';\n\n\t/**\n\t * Type of watermarking.  Options:  text/overlay\n\t *\n\t * @var string\n\t */\n\tpublic $wm_type\t\t\t= 'text';\n\n\t/**\n\t * Default transparency for watermark\n\t *\n\t * @var int\n\t */\n\tpublic $wm_x_transp\t\t= 4;\n\n\t/**\n\t * Default transparency for watermark\n\t *\n\t * @var int\n\t */\n\tpublic $wm_y_transp\t\t= 4;\n\n\t/**\n\t * Watermark image path\n\t *\n\t * @var string\n\t */\n\tpublic $wm_overlay_path\t\t= '';\n\n\t/**\n\t * TT font\n\t *\n\t * @var string\n\t */\n\tpublic $wm_font_path\t\t= '';\n\n\t/**\n\t * Font size (different versions of GD will either use points or pixels)\n\t *\n\t * @var int\n\t */\n\tpublic $wm_font_size\t\t= 17;\n\n\t/**\n\t * Vertical alignment:   T M B\n\t *\n\t * @var string\n\t */\n\tpublic $wm_vrt_alignment\t= 'B';\n\n\t/**\n\t * Horizontal alignment: L R C\n\t *\n\t * @var string\n\t */\n\tpublic $wm_hor_alignment\t= 'C';\n\n\t/**\n\t * Padding around text\n\t *\n\t * @var int\n\t */\n\tpublic $wm_padding\t\t\t= 0;\n\n\t/**\n\t * Lets you push text to the right\n\t *\n\t * @var int\n\t */\n\tpublic $wm_hor_offset\t\t= 0;\n\n\t/**\n\t * Lets you push text down\n\t *\n\t * @var int\n\t */\n\tpublic $wm_vrt_offset\t\t= 0;\n\n\t/**\n\t * Text color\n\t *\n\t * @var string\n\t */\n\tprotected $wm_font_color\t= '#ffffff';\n\n\t/**\n\t * Dropshadow color\n\t *\n\t * @var string\n\t */\n\tprotected $wm_shadow_color\t= '';\n\n\t/**\n\t * Dropshadow distance\n\t *\n\t * @var int\n\t */\n\tpublic $wm_shadow_distance\t= 2;\n\n\t/**\n\t * Image opacity: 1 - 100  Only works with image\n\t *\n\t * @var int\n\t */\n\tpublic $wm_opacity\t\t= 50;\n\n\t// --------------------------------------------------------------------------\n\t// Private Vars\n\t// --------------------------------------------------------------------------\n\n\t/**\n\t * Source image folder\n\t *\n\t * @var string\n\t */\n\tpublic $source_folder\t\t= '';\n\n\t/**\n\t * Destination image folder\n\t *\n\t * @var string\n\t */\n\tpublic $dest_folder\t\t= '';\n\n\t/**\n\t * Image mime-type\n\t *\n\t * @var string\n\t */\n\tpublic $mime_type\t\t= '';\n\n\t/**\n\t * Original image width\n\t *\n\t * @var int\n\t */\n\tpublic $orig_width\t\t= '';\n\n\t/**\n\t * Original image height\n\t *\n\t * @var int\n\t */\n\tpublic $orig_height\t\t= '';\n\n\t/**\n\t * Image format\n\t *\n\t * @var string\n\t */\n\tpublic $image_type\t\t= '';\n\n\t/**\n\t * Size of current image\n\t *\n\t * @var string\n\t */\n\tpublic $size_str\t\t= '';\n\n\t/**\n\t * Full path to source image\n\t *\n\t * @var string\n\t */\n\tpublic $full_src_path\t\t= '';\n\n\t/**\n\t * Full path to destination image\n\t *\n\t * @var string\n\t */\n\tpublic $full_dst_path\t\t= '';\n\n\t/**\n\t * File permissions\n\t *\n\t * @var\tint\n\t */\n\tpublic $file_permissions = 0644;\n\n\t/**\n\t * Name of function to create image\n\t *\n\t * @var string\n\t */\n\tpublic $create_fnc\t\t= 'imagecreatetruecolor';\n\n\t/**\n\t * Name of function to copy image\n\t *\n\t * @var string\n\t */\n\tpublic $copy_fnc\t\t= 'imagecopyresampled';\n\n\t/**\n\t * Error messages\n\t *\n\t * @var array\n\t */\n\tpublic $error_msg\t\t= array();\n\n\t/**\n\t * Whether to have a drop shadow on watermark\n\t *\n\t * @var bool\n\t */\n\tprotected $wm_use_drop_shadow\t= FALSE;\n\n\t/**\n\t * Whether to use truetype fonts\n\t *\n\t * @var bool\n\t */\n\tpublic $wm_use_truetype\t= FALSE;\n\n\t/**\n\t * Initialize Image Library\n\t *\n\t * @param\tarray\t$props\n\t * @return\tvoid\n\t */\n\tpublic function __construct($props = array())\n\t{\n\t\tif (count($props) > 0)\n\t\t{\n\t\t\t$this->initialize($props);\n\t\t}\n\n\t\t/**\n\t\t * A work-around for some improperly formatted, but\n\t\t * usable JPEGs; known to be produced by Samsung\n\t\t * smartphones' front-facing cameras.\n\t\t *\n\t\t * @see\thttps://github.com/bcit-ci/CodeIgniter/issues/4967\n\t\t * @see\thttps://bugs.php.net/bug.php?id=72404\n\t\t */\n\t\tini_set('gd.jpeg_ignore_warning', 1);\n\n\t\tlog_message('info', 'Image Lib Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Initialize image properties\n\t *\n\t * Resets values in case this class is used in a loop\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function clear()\n\t{\n\t\t$props = array('thumb_marker', 'library_path', 'source_image', 'new_image', 'width', 'height', 'rotation_angle', 'x_axis', 'y_axis', 'wm_text', 'wm_overlay_path', 'wm_font_path', 'wm_shadow_color', 'source_folder', 'dest_folder', 'mime_type', 'orig_width', 'orig_height', 'image_type', 'size_str', 'full_src_path', 'full_dst_path');\n\n\t\tforeach ($props as $val)\n\t\t{\n\t\t\t$this->$val = '';\n\t\t}\n\n\t\t$this->image_library \t\t= 'gd2';\n\t\t$this->dynamic_output \t\t= FALSE;\n\t\t$this->quality \t\t\t\t= 90;\n\t\t$this->create_thumb \t\t= FALSE;\n\t\t$this->thumb_marker \t\t= '_thumb';\n\t\t$this->maintain_ratio \t\t= TRUE;\n\t\t$this->master_dim \t\t\t= 'auto';\n\t\t$this->wm_type \t\t\t\t= 'text';\n\t\t$this->wm_x_transp \t\t\t= 4;\n\t\t$this->wm_y_transp \t\t\t= 4;\n\t\t$this->wm_font_size \t\t= 17;\n\t\t$this->wm_vrt_alignment \t= 'B';\n\t\t$this->wm_hor_alignment \t= 'C';\n\t\t$this->wm_padding \t\t\t= 0;\n\t\t$this->wm_hor_offset \t\t= 0;\n\t\t$this->wm_vrt_offset \t\t= 0;\n\t\t$this->wm_font_color\t\t= '#ffffff';\n\t\t$this->wm_shadow_distance \t= 2;\n\t\t$this->wm_opacity \t\t\t= 50;\n\t\t$this->create_fnc \t\t\t= 'imagecreatetruecolor';\n\t\t$this->copy_fnc \t\t\t= 'imagecopyresampled';\n\t\t$this->error_msg \t\t\t= array();\n\t\t$this->wm_use_drop_shadow \t= FALSE;\n\t\t$this->wm_use_truetype \t\t= FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * initialize image preferences\n\t *\n\t * @param\tarray\n\t * @return\tbool\n\t */\n\tpublic function initialize($props = array())\n\t{\n\t\t// Convert array elements into class variables\n\t\tif (count($props) > 0)\n\t\t{\n\t\t\tforeach ($props as $key => $val)\n\t\t\t{\n\t\t\t\tif (property_exists($this, $key))\n\t\t\t\t{\n\t\t\t\t\tif (in_array($key, array('wm_font_color', 'wm_shadow_color'), TRUE))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (preg_match('/^#?([0-9a-f]{3}|[0-9a-f]{6})$/i', $val, $matches))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t/* $matches[1] contains our hex color value, but it might be\n\t\t\t\t\t\t\t * both in the full 6-length format or the shortened 3-length\n\t\t\t\t\t\t\t * value.\n\t\t\t\t\t\t\t * We'll later need the full version, so we keep it if it's\n\t\t\t\t\t\t\t * already there and if not - we'll convert to it. We can\n\t\t\t\t\t\t\t * access string characters by their index as in an array,\n\t\t\t\t\t\t\t * so we'll do that and use concatenation to form the final\n\t\t\t\t\t\t\t * value:\n\t\t\t\t\t\t\t */\n\t\t\t\t\t\t\t$val = (strlen($matches[1]) === 6)\n\t\t\t\t\t\t\t\t? '#'.$matches[1]\n\t\t\t\t\t\t\t\t: '#'.$matches[1][0].$matches[1][0].$matches[1][1].$matches[1][1].$matches[1][2].$matches[1][2];\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telseif (in_array($key, array('width', 'height'), TRUE) && ! ctype_digit((string) $val))\n\t\t\t\t\t{\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t$this->$key = $val;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Is there a source image? If not, there's no reason to continue\n\t\tif ($this->source_image === '')\n\t\t{\n\t\t\t$this->set_error('imglib_source_image_required');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t/* Is getimagesize() available?\n\t\t *\n\t\t * We use it to determine the image properties (width/height).\n\t\t * Note: We need to figure out how to determine image\n\t\t * properties using ImageMagick and NetPBM\n\t\t */\n\t\tif ( ! function_exists('getimagesize'))\n\t\t{\n\t\t\t$this->set_error('imglib_gd_required_for_props');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$this->image_library = strtolower($this->image_library);\n\n\t\t/* Set the full server path\n\t\t *\n\t\t * The source image may or may not contain a path.\n\t\t * Either way, we'll try use realpath to generate the\n\t\t * full server path in order to more reliably read it.\n\t\t */\n\t\tif (($full_source_path = realpath($this->source_image)) !== FALSE)\n\t\t{\n\t\t\t$full_source_path = str_replace('\\\\', '/', $full_source_path);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$full_source_path = $this->source_image;\n\t\t}\n\n\t\t$x = explode('/', $full_source_path);\n\t\t$this->source_image = end($x);\n\t\t$this->source_folder = str_replace($this->source_image, '', $full_source_path);\n\n\t\t// Set the Image Properties\n\t\tif ( ! $this->get_image_properties($this->source_folder.$this->source_image))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t/*\n\t\t * Assign the \"new\" image name/path\n\t\t *\n\t\t * If the user has set a \"new_image\" name it means\n\t\t * we are making a copy of the source image. If not\n\t\t * it means we are altering the original. We'll\n\t\t * set the destination filename and path accordingly.\n\t\t */\n\t\tif ($this->new_image === '')\n\t\t{\n\t\t\t$this->dest_image  = $this->source_image;\n\t\t\t$this->dest_folder = $this->source_folder;\n\t\t}\n\t\telseif (strpos($this->new_image, '/') === FALSE && strpos($this->new_image, '\\\\') === FALSE)\n\t\t{\n\t\t\t$this->dest_image  = $this->new_image;\n\t\t\t$this->dest_folder = $this->source_folder;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Is there a file name?\n\t\t\tif ( ! preg_match('#\\.(jpg|jpeg|gif|png)$#i', $this->new_image))\n\t\t\t{\n\t\t\t\t$this->dest_image  = $this->source_image;\n\t\t\t\t$this->dest_folder = $this->new_image;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$x = explode('/', str_replace('\\\\', '/', $this->new_image));\n\t\t\t\t$this->dest_image  = end($x);\n\t\t\t\t$this->dest_folder = str_replace($this->dest_image, '', $this->new_image);\n\t\t\t}\n\n\t\t\t$this->dest_folder = realpath($this->dest_folder).'/';\n\t\t}\n\n\t\t/* Compile the finalized filenames/paths\n\t\t *\n\t\t * We'll create two master strings containing the\n\t\t * full server path to the source image and the\n\t\t * full server path to the destination image.\n\t\t * We'll also split the destination image name\n\t\t * so we can insert the thumbnail marker if needed.\n\t\t */\n\t\tif ($this->create_thumb === FALSE OR $this->thumb_marker === '')\n\t\t{\n\t\t\t$this->thumb_marker = '';\n\t\t}\n\n\t\t$xp = $this->explode_name($this->dest_image);\n\n\t\t$filename = $xp['name'];\n\t\t$file_ext = $xp['ext'];\n\n\t\t$this->full_src_path = $this->source_folder.$this->source_image;\n\t\t$this->full_dst_path = $this->dest_folder.$filename.$this->thumb_marker.$file_ext;\n\n\t\t/* Should we maintain image proportions?\n\t\t *\n\t\t * When creating thumbs or copies, the target width/height\n\t\t * might not be in correct proportion with the source\n\t\t * image's width/height. We'll recalculate it here.\n\t\t */\n\t\tif ($this->maintain_ratio === TRUE && ($this->width !== 0 OR $this->height !== 0))\n\t\t{\n\t\t\t$this->image_reproportion();\n\t\t}\n\n\t\t/* Was a width and height specified?\n\t\t *\n\t\t * If the destination width/height was not submitted we\n\t\t * will use the values from the actual file\n\t\t */\n\t\tif ($this->width === '')\n\t\t{\n\t\t\t$this->width = $this->orig_width;\n\t\t}\n\n\t\tif ($this->height === '')\n\t\t{\n\t\t\t$this->height = $this->orig_height;\n\t\t}\n\n\t\t// Set the quality\n\t\t$this->quality = trim(str_replace('%', '', $this->quality));\n\n\t\tif ($this->quality === '' OR $this->quality === 0 OR ! ctype_digit($this->quality))\n\t\t{\n\t\t\t$this->quality = 90;\n\t\t}\n\n\t\t// Set the x/y coordinates\n\t\tis_numeric($this->x_axis) OR $this->x_axis = 0;\n\t\tis_numeric($this->y_axis) OR $this->y_axis = 0;\n\n\t\t// Watermark-related Stuff...\n\t\tif ($this->wm_overlay_path !== '')\n\t\t{\n\t\t\t$this->wm_overlay_path = str_replace('\\\\', '/', realpath($this->wm_overlay_path));\n\t\t}\n\n\t\tif ($this->wm_shadow_color !== '')\n\t\t{\n\t\t\t$this->wm_use_drop_shadow = TRUE;\n\t\t}\n\t\telseif ($this->wm_use_drop_shadow === TRUE && $this->wm_shadow_color === '')\n\t\t{\n\t\t\t$this->wm_use_drop_shadow = FALSE;\n\t\t}\n\n\t\tif ($this->wm_font_path !== '')\n\t\t{\n\t\t\t$this->wm_use_truetype = TRUE;\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Image Resize\n\t *\n\t * This is a wrapper function that chooses the proper\n\t * resize function based on the protocol specified\n\t *\n\t * @return\tbool\n\t */\n\tpublic function resize()\n\t{\n\t\t$protocol = ($this->image_library === 'gd2') ? 'image_process_gd' : 'image_process_'.$this->image_library;\n\t\treturn $this->$protocol('resize');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Image Crop\n\t *\n\t * This is a wrapper function that chooses the proper\n\t * cropping function based on the protocol specified\n\t *\n\t * @return\tbool\n\t */\n\tpublic function crop()\n\t{\n\t\t$protocol = ($this->image_library === 'gd2') ? 'image_process_gd' : 'image_process_'.$this->image_library;\n\t\treturn $this->$protocol('crop');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Image Rotate\n\t *\n\t * This is a wrapper function that chooses the proper\n\t * rotation function based on the protocol specified\n\t *\n\t * @return\tbool\n\t */\n\tpublic function rotate()\n\t{\n\t\t// Allowed rotation values\n\t\t$degs = array(90, 180, 270, 'vrt', 'hor');\n\n\t\tif ($this->rotation_angle === '' OR ! in_array($this->rotation_angle, $degs))\n\t\t{\n\t\t\t$this->set_error('imglib_rotation_angle_required');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Reassign the width and height\n\t\tif ($this->rotation_angle === 90 OR $this->rotation_angle === 270)\n\t\t{\n\t\t\t$this->width\t= $this->orig_height;\n\t\t\t$this->height\t= $this->orig_width;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$this->width\t= $this->orig_width;\n\t\t\t$this->height\t= $this->orig_height;\n\t\t}\n\n\t\t// Choose resizing function\n\t\tif ($this->image_library === 'imagemagick' OR $this->image_library === 'netpbm')\n\t\t{\n\t\t\t$protocol = 'image_process_'.$this->image_library;\n\t\t\treturn $this->$protocol('rotate');\n\t\t}\n\n\t\treturn ($this->rotation_angle === 'hor' OR $this->rotation_angle === 'vrt')\n\t\t\t? $this->image_mirror_gd()\n\t\t\t: $this->image_rotate_gd();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Image Process Using GD/GD2\n\t *\n\t * This function will resize or crop\n\t *\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function image_process_gd($action = 'resize')\n\t{\n\t\t$v2_override = FALSE;\n\n\t\t// If the target width/height match the source, AND if the new file name is not equal to the old file name\n\t\t// we'll simply make a copy of the original with the new name... assuming dynamic rendering is off.\n\t\tif ($this->dynamic_output === FALSE && $this->orig_width === $this->width && $this->orig_height === $this->height)\n\t\t{\n\t\t\tif ($this->source_image !== $this->new_image && @copy($this->full_src_path, $this->full_dst_path))\n\t\t\t{\n\t\t\t\tchmod($this->full_dst_path, $this->file_permissions);\n\t\t\t}\n\n\t\t\treturn TRUE;\n\t\t}\n\n\t\t// Let's set up our values based on the action\n\t\tif ($action === 'crop')\n\t\t{\n\t\t\t// Reassign the source width/height if cropping\n\t\t\t$this->orig_width  = $this->width;\n\t\t\t$this->orig_height = $this->height;\n\n\t\t\t// GD 2.0 has a cropping bug so we'll test for it\n\t\t\tif ($this->gd_version() !== FALSE)\n\t\t\t{\n\t\t\t\t$gd_version = str_replace('0', '', $this->gd_version());\n\t\t\t\t$v2_override = ($gd_version == 2);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// If resizing the x/y axis must be zero\n\t\t\t$this->x_axis = 0;\n\t\t\t$this->y_axis = 0;\n\t\t}\n\n\t\t// Create the image handle\n\t\tif ( ! ($src_img = $this->image_create_gd()))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t/* Create the image\n\t\t *\n\t\t * Old conditional which users report cause problems with shared GD libs who report themselves as \"2.0 or greater\"\n\t\t * it appears that this is no longer the issue that it was in 2004, so we've removed it, retaining it in the comment\n\t\t * below should that ever prove inaccurate.\n\t\t *\n\t\t * if ($this->image_library === 'gd2' && function_exists('imagecreatetruecolor') && $v2_override === FALSE)\n\t\t */\n\t\tif ($this->image_library === 'gd2' && function_exists('imagecreatetruecolor'))\n\t\t{\n\t\t\t$create\t= 'imagecreatetruecolor';\n\t\t\t$copy\t= 'imagecopyresampled';\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$create\t= 'imagecreate';\n\t\t\t$copy\t= 'imagecopyresized';\n\t\t}\n\n\t\t$dst_img = $create($this->width, $this->height);\n\n\t\tif ($this->image_type === 3) // png we can actually preserve transparency\n\t\t{\n\t\t\timagealphablending($dst_img, FALSE);\n\t\t\timagesavealpha($dst_img, TRUE);\n\t\t}\n\n\t\t$copy($dst_img, $src_img, 0, 0, $this->x_axis, $this->y_axis, $this->width, $this->height, $this->orig_width, $this->orig_height);\n\n\t\t// Show the image\n\t\tif ($this->dynamic_output === TRUE)\n\t\t{\n\t\t\t$this->image_display_gd($dst_img);\n\t\t}\n\t\telseif ( ! $this->image_save_gd($dst_img)) // Or save it\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Kill the file handles\n\t\timagedestroy($dst_img);\n\t\timagedestroy($src_img);\n\n\t\tif ($this->dynamic_output !== TRUE)\n\t\t{\n\t\t\tchmod($this->full_dst_path, $this->file_permissions);\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Image Process Using ImageMagick\n\t *\n\t * This function will resize, crop or rotate\n\t *\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function image_process_imagemagick($action = 'resize')\n\t{\n\t\t// Do we have a vaild library path?\n\t\tif ($this->library_path === '')\n\t\t{\n\t\t\t$this->set_error('imglib_libpath_invalid');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif ( ! preg_match('/convert$/i', $this->library_path))\n\t\t{\n\t\t\t$this->library_path = rtrim($this->library_path, '/').'/convert';\n\t\t}\n\n\t\t// Execute the command\n\t\t$cmd = $this->library_path.' -quality '.$this->quality;\n\n\t\tif ($action === 'crop')\n\t\t{\n\t\t\t$cmd .= ' -crop '.$this->width.'x'.$this->height.'+'.$this->x_axis.'+'.$this->y_axis;\n\t\t}\n\t\telseif ($action === 'rotate')\n\t\t{\n\t\t\t$cmd .= ($this->rotation_angle === 'hor' OR $this->rotation_angle === 'vrt')\n\t\t\t\t\t? ' -flop'\n\t\t\t\t\t: ' -rotate '.$this->rotation_angle;\n\t\t}\n\t\telse // Resize\n\t\t{\n\t\t\tif($this->maintain_ratio === TRUE)\n\t\t\t{\n\t\t\t\t$cmd .= ' -resize '.$this->width.'x'.$this->height;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$cmd .= ' -resize '.$this->width.'x'.$this->height.'\\!';\n\t\t\t}\n\t\t}\n\n\t\t$cmd .= ' '.escapeshellarg($this->full_src_path).' '.escapeshellarg($this->full_dst_path).' 2>&1';\n\n\t\t$retval = 1;\n\t\t// exec() might be disabled\n\t\tif (function_usable('exec'))\n\t\t{\n\t\t\t@exec($cmd, $output, $retval);\n\t\t}\n\n\t\t// Did it work?\n\t\tif ($retval > 0)\n\t\t{\n\t\t\t$this->set_error('imglib_image_process_failed');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tchmod($this->full_dst_path, $this->file_permissions);\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Image Process Using NetPBM\n\t *\n\t * This function will resize, crop or rotate\n\t *\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function image_process_netpbm($action = 'resize')\n\t{\n\t\tif ($this->library_path === '')\n\t\t{\n\t\t\t$this->set_error('imglib_libpath_invalid');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Build the resizing command\n\t\tswitch ($this->image_type)\n\t\t{\n\t\t\tcase 1 :\n\t\t\t\t$cmd_in\t\t= 'giftopnm';\n\t\t\t\t$cmd_out\t= 'ppmtogif';\n\t\t\t\tbreak;\n\t\t\tcase 2 :\n\t\t\t\t$cmd_in\t\t= 'jpegtopnm';\n\t\t\t\t$cmd_out\t= 'ppmtojpeg';\n\t\t\t\tbreak;\n\t\t\tcase 3 :\n\t\t\t\t$cmd_in\t\t= 'pngtopnm';\n\t\t\t\t$cmd_out\t= 'ppmtopng';\n\t\t\t\tbreak;\n\t\t\tcase 18 :\n\t\t\t\t$cmd_in\t\t= 'webptopnm';\n\t\t\t\t$cmd_out\t= 'ppmtowebp';\n\t\t\t\tbreak;\n\t\t}\n\n\t\tif ($action === 'crop')\n\t\t{\n\t\t\t$cmd_inner = 'pnmcut -left '.$this->x_axis.' -top '.$this->y_axis.' -width '.$this->width.' -height '.$this->height;\n\t\t}\n\t\telseif ($action === 'rotate')\n\t\t{\n\t\t\tswitch ($this->rotation_angle)\n\t\t\t{\n\t\t\t\tcase 90:\t$angle = 'r270';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 180:\t$angle = 'r180';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 270:\t$angle = 'r90';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'vrt':\t$angle = 'tb';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'hor':\t$angle = 'lr';\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t$cmd_inner = 'pnmflip -'.$angle.' ';\n\t\t}\n\t\telse // Resize\n\t\t{\n\t\t\t$cmd_inner = 'pnmscale -xysize '.$this->width.' '.$this->height;\n\t\t}\n\n\t\t$cmd = $this->library_path.$cmd_in.' '.escapeshellarg($this->full_src_path).' | '.$cmd_inner.' | '.$cmd_out.' > '.$this->dest_folder.'netpbm.tmp';\n\n\t\t$retval = 1;\n\t\t// exec() might be disabled\n\t\tif (function_usable('exec'))\n\t\t{\n\t\t\t@exec($cmd, $output, $retval);\n\t\t}\n\n\t\t// Did it work?\n\t\tif ($retval > 0)\n\t\t{\n\t\t\t$this->set_error('imglib_image_process_failed');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// With NetPBM we have to create a temporary image.\n\t\t// If you try manipulating the original it fails so\n\t\t// we have to rename the temp file.\n\t\tcopy($this->dest_folder.'netpbm.tmp', $this->full_dst_path);\n\t\tunlink($this->dest_folder.'netpbm.tmp');\n\t\tchmod($this->full_dst_path, $this->file_permissions);\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Image Rotate Using GD\n\t *\n\t * @return\tbool\n\t */\n\tpublic function image_rotate_gd()\n\t{\n\t\t// Create the image handle\n\t\tif ( ! ($src_img = $this->image_create_gd()))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Set the background color\n\t\t// This won't work with transparent PNG files so we are\n\t\t// going to have to figure out how to determine the color\n\t\t// of the alpha channel in a future release.\n\n\t\t$white = imagecolorallocate($src_img, 255, 255, 255);\n\n\t\t// Rotate it!\n\t\t$dst_img = imagerotate($src_img, $this->rotation_angle, $white);\n\n\t\t// Show the image\n\t\tif ($this->dynamic_output === TRUE)\n\t\t{\n\t\t\t$this->image_display_gd($dst_img);\n\t\t}\n\t\telseif ( ! $this->image_save_gd($dst_img)) // ... or save it\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Kill the file handles\n\t\timagedestroy($dst_img);\n\t\timagedestroy($src_img);\n\n\t\tchmod($this->full_dst_path, $this->file_permissions);\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Create Mirror Image using GD\n\t *\n\t * This function will flip horizontal or vertical\n\t *\n\t * @return\tbool\n\t */\n\tpublic function image_mirror_gd()\n\t{\n\t\tif ( ! $src_img = $this->image_create_gd())\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$width  = $this->orig_width;\n\t\t$height = $this->orig_height;\n\n\t\tif ($this->rotation_angle === 'hor')\n\t\t{\n\t\t\tfor ($i = 0; $i < $height; $i++)\n\t\t\t{\n\t\t\t\t$left = 0;\n\t\t\t\t$right = $width - 1;\n\n\t\t\t\twhile ($left < $right)\n\t\t\t\t{\n\t\t\t\t\t$cl = imagecolorat($src_img, $left, $i);\n\t\t\t\t\t$cr = imagecolorat($src_img, $right, $i);\n\n\t\t\t\t\timagesetpixel($src_img, $left, $i, $cr);\n\t\t\t\t\timagesetpixel($src_img, $right, $i, $cl);\n\n\t\t\t\t\t$left++;\n\t\t\t\t\t$right--;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor ($i = 0; $i < $width; $i++)\n\t\t\t{\n\t\t\t\t$top = 0;\n\t\t\t\t$bottom = $height - 1;\n\n\t\t\t\twhile ($top < $bottom)\n\t\t\t\t{\n\t\t\t\t\t$ct = imagecolorat($src_img, $i, $top);\n\t\t\t\t\t$cb = imagecolorat($src_img, $i, $bottom);\n\n\t\t\t\t\timagesetpixel($src_img, $i, $top, $cb);\n\t\t\t\t\timagesetpixel($src_img, $i, $bottom, $ct);\n\n\t\t\t\t\t$top++;\n\t\t\t\t\t$bottom--;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Show the image\n\t\tif ($this->dynamic_output === TRUE)\n\t\t{\n\t\t\t$this->image_display_gd($src_img);\n\t\t}\n\t\telseif ( ! $this->image_save_gd($src_img)) // ... or save it\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Kill the file handles\n\t\timagedestroy($src_img);\n\n\t\tchmod($this->full_dst_path, $this->file_permissions);\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Image Watermark\n\t *\n\t * This is a wrapper function that chooses the type\n\t * of watermarking based on the specified preference.\n\t *\n\t * @return\tbool\n\t */\n\tpublic function watermark()\n\t{\n\t\treturn ($this->wm_type === 'overlay') ? $this->overlay_watermark() : $this->text_watermark();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Watermark - Graphic Version\n\t *\n\t * @return\tbool\n\t */\n\tpublic function overlay_watermark()\n\t{\n\t\tif ( ! function_exists('imagecolortransparent'))\n\t\t{\n\t\t\t$this->set_error('imglib_gd_required');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Fetch source image properties\n\t\t$this->get_image_properties();\n\n\t\t// Fetch watermark image properties\n\t\t$props\t\t= $this->get_image_properties($this->wm_overlay_path, TRUE);\n\t\t$wm_img_type\t= $props['image_type'];\n\t\t$wm_width\t= $props['width'];\n\t\t$wm_height\t= $props['height'];\n\n\t\t// Create two image resources\n\t\t$wm_img  = $this->image_create_gd($this->wm_overlay_path, $wm_img_type);\n\t\t$src_img = $this->image_create_gd($this->full_src_path);\n\n\t\t// Reverse the offset if necessary\n\t\t// When the image is positioned at the bottom\n\t\t// we don't want the vertical offset to push it\n\t\t// further down. We want the reverse, so we'll\n\t\t// invert the offset. Same with the horizontal\n\t\t// offset when the image is at the right\n\n\t\t$this->wm_vrt_alignment = strtoupper($this->wm_vrt_alignment[0]);\n\t\t$this->wm_hor_alignment = strtoupper($this->wm_hor_alignment[0]);\n\n\t\tif ($this->wm_vrt_alignment === 'B')\n\t\t\t$this->wm_vrt_offset = $this->wm_vrt_offset * -1;\n\n\t\tif ($this->wm_hor_alignment === 'R')\n\t\t\t$this->wm_hor_offset = $this->wm_hor_offset * -1;\n\n\t\t// Set the base x and y axis values\n\t\t$x_axis = $this->wm_hor_offset + $this->wm_padding;\n\t\t$y_axis = $this->wm_vrt_offset + $this->wm_padding;\n\n\t\t// Set the vertical position\n\t\tif ($this->wm_vrt_alignment === 'M')\n\t\t{\n\t\t\t$y_axis += ($this->orig_height / 2) - ($wm_height / 2);\n\t\t}\n\t\telseif ($this->wm_vrt_alignment === 'B')\n\t\t{\n\t\t\t$y_axis += $this->orig_height - $wm_height;\n\t\t}\n\n\t\t// Set the horizontal position\n\t\tif ($this->wm_hor_alignment === 'C')\n\t\t{\n\t\t\t$x_axis += ($this->orig_width / 2) - ($wm_width / 2);\n\t\t}\n\t\telseif ($this->wm_hor_alignment === 'R')\n\t\t{\n\t\t\t$x_axis += $this->orig_width - $wm_width;\n\t\t}\n\n\t\t// Build the finalized image\n\t\tif ($wm_img_type === 3)\n\t\t{\n\t\t\t@imagealphablending($src_img, TRUE);\n\t\t}\n\n\t\t// Set RGB values for text and shadow\n\t\t$rgba = imagecolorat($wm_img, $this->wm_x_transp, $this->wm_y_transp);\n\t\t$alpha = ($rgba & 0x7F000000) >> 24;\n\n\t\t// make a best guess as to whether we're dealing with an image with alpha transparency or no/binary transparency\n\t\tif ($alpha > 0)\n\t\t{\n\t\t\t// copy the image directly, the image's alpha transparency being the sole determinant of blending\n\t\t\timagecopy($src_img, $wm_img, $x_axis, $y_axis, 0, 0, $wm_width, $wm_height);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// set our RGB value from above to be transparent and merge the images with the specified opacity\n\t\t\timagecolortransparent($wm_img, imagecolorat($wm_img, $this->wm_x_transp, $this->wm_y_transp));\n\t\t\timagecopymerge($src_img, $wm_img, $x_axis, $y_axis, 0, 0, $wm_width, $wm_height, $this->wm_opacity);\n\t\t}\n\n\t\t// We can preserve transparency for PNG images\n\t\tif ($this->image_type === 3)\n\t\t{\n\t\t\timagealphablending($src_img, FALSE);\n\t\t\timagesavealpha($src_img, TRUE);\n\t\t}\n\n\t\t// Output the image\n\t\tif ($this->dynamic_output === TRUE)\n\t\t{\n\t\t\t$this->image_display_gd($src_img);\n\t\t}\n\t\telseif ( ! $this->image_save_gd($src_img)) // ... or save it\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\timagedestroy($src_img);\n\t\timagedestroy($wm_img);\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Watermark - Text Version\n\t *\n\t * @return\tbool\n\t */\n\tpublic function text_watermark()\n\t{\n\t\tif ( ! ($src_img = $this->image_create_gd()))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif ($this->wm_use_truetype === TRUE && ! file_exists($this->wm_font_path))\n\t\t{\n\t\t\t$this->set_error('imglib_missing_font');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Fetch source image properties\n\t\t$this->get_image_properties();\n\n\t\t// Reverse the vertical offset\n\t\t// When the image is positioned at the bottom\n\t\t// we don't want the vertical offset to push it\n\t\t// further down. We want the reverse, so we'll\n\t\t// invert the offset. Note: The horizontal\n\t\t// offset flips itself automatically\n\n\t\tif ($this->wm_vrt_alignment === 'B')\n\t\t{\n\t\t\t$this->wm_vrt_offset = $this->wm_vrt_offset * -1;\n\t\t}\n\n\t\tif ($this->wm_hor_alignment === 'R')\n\t\t{\n\t\t\t$this->wm_hor_offset = $this->wm_hor_offset * -1;\n\t\t}\n\n\t\t// Set font width and height\n\t\t// These are calculated differently depending on\n\t\t// whether we are using the true type font or not\n\t\tif ($this->wm_use_truetype === TRUE)\n\t\t{\n\t\t\tif (empty($this->wm_font_size))\n\t\t\t{\n\t\t\t\t$this->wm_font_size = 17;\n\t\t\t}\n\n\t\t\tif (function_exists('imagettfbbox'))\n\t\t\t{\n\t\t\t\t$temp = imagettfbbox($this->wm_font_size, 0, $this->wm_font_path, $this->wm_text);\n\t\t\t\t$temp = $temp[2] - $temp[0];\n\n\t\t\t\t$fontwidth = $temp / strlen($this->wm_text);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$fontwidth = $this->wm_font_size - ($this->wm_font_size / 4);\n\t\t\t}\n\n\t\t\t$fontheight = $this->wm_font_size;\n\t\t\t$this->wm_vrt_offset += $this->wm_font_size;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$fontwidth  = imagefontwidth($this->wm_font_size);\n\t\t\t$fontheight = imagefontheight($this->wm_font_size);\n\t\t}\n\n\t\t// Set base X and Y axis values\n\t\t$x_axis = $this->wm_hor_offset + $this->wm_padding;\n\t\t$y_axis = $this->wm_vrt_offset + $this->wm_padding;\n\n\t\tif ($this->wm_use_drop_shadow === FALSE)\n\t\t{\n\t\t\t$this->wm_shadow_distance = 0;\n\t\t}\n\n\t\t$this->wm_vrt_alignment = strtoupper($this->wm_vrt_alignment[0]);\n\t\t$this->wm_hor_alignment = strtoupper($this->wm_hor_alignment[0]);\n\n\t\t// Set vertical alignment\n\t\tif ($this->wm_vrt_alignment === 'M')\n\t\t{\n\t\t\t$y_axis += ($this->orig_height / 2) + ($fontheight / 2);\n\t\t}\n\t\telseif ($this->wm_vrt_alignment === 'B')\n\t\t{\n\t\t\t$y_axis += $this->orig_height - $fontheight - $this->wm_shadow_distance - ($fontheight / 2);\n\t\t}\n\n\t\t// Set horizontal alignment\n\t\tif ($this->wm_hor_alignment === 'R')\n\t\t{\n\t\t\t$x_axis += $this->orig_width - ($fontwidth * strlen($this->wm_text)) - $this->wm_shadow_distance;\n\t\t}\n\t\telseif ($this->wm_hor_alignment === 'C')\n\t\t{\n\t\t\t$x_axis += floor(($this->orig_width - ($fontwidth * strlen($this->wm_text))) / 2);\n\t\t}\n\n\t\tif ($this->wm_use_drop_shadow)\n\t\t{\n\t\t\t// Offset from text\n\t\t\t$x_shad = $x_axis + $this->wm_shadow_distance;\n\t\t\t$y_shad = $y_axis + $this->wm_shadow_distance;\n\n\t\t\t/* Set RGB values for shadow\n\t\t\t *\n\t\t\t * First character is #, so we don't really need it.\n\t\t\t * Get the rest of the string and split it into 2-length\n\t\t\t * hex values:\n\t\t\t */\n\t\t\t$drp_color = str_split(substr($this->wm_shadow_color, 1, 6), 2);\n\t\t\t$drp_color = imagecolorclosest($src_img, hexdec($drp_color[0]), hexdec($drp_color[1]), hexdec($drp_color[2]));\n\n\t\t\t// Add the shadow to the source image\n\t\t\tif ($this->wm_use_truetype)\n\t\t\t{\n\t\t\t\timagettftext($src_img, $this->wm_font_size, 0, $x_shad, $y_shad, $drp_color, $this->wm_font_path, $this->wm_text);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\timagestring($src_img, $this->wm_font_size, $x_shad, $y_shad, $this->wm_text, $drp_color);\n\t\t\t}\n\t\t}\n\n\t\t/* Set RGB values for text\n\t\t *\n\t\t * First character is #, so we don't really need it.\n\t\t * Get the rest of the string and split it into 2-length\n\t\t * hex values:\n\t\t */\n\t\t$txt_color = str_split(substr($this->wm_font_color, 1, 6), 2);\n\t\t$txt_color = imagecolorclosest($src_img, hexdec($txt_color[0]), hexdec($txt_color[1]), hexdec($txt_color[2]));\n\n\t\t// Add the text to the source image\n\t\tif ($this->wm_use_truetype)\n\t\t{\n\t\t\timagettftext($src_img, $this->wm_font_size, 0, $x_axis, $y_axis, $txt_color, $this->wm_font_path, $this->wm_text);\n\t\t}\n\t\telse\n\t\t{\n\t\t\timagestring($src_img, $this->wm_font_size, $x_axis, $y_axis, $this->wm_text, $txt_color);\n\t\t}\n\n\t\t// We can preserve transparency for PNG images\n\t\tif ($this->image_type === 3)\n\t\t{\n\t\t\timagealphablending($src_img, FALSE);\n\t\t\timagesavealpha($src_img, TRUE);\n\t\t}\n\n\t\t// Output the final image\n\t\tif ($this->dynamic_output === TRUE)\n\t\t{\n\t\t\t$this->image_display_gd($src_img);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$this->image_save_gd($src_img);\n\t\t}\n\n\t\timagedestroy($src_img);\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Create Image - GD\n\t *\n\t * This simply creates an image resource handle\n\t * based on the type of image being processed\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tresource\n\t */\n\tpublic function image_create_gd($path = '', $image_type = '')\n\t{\n\t\tif ($path === '')\n\t\t{\n\t\t\t$path = $this->full_src_path;\n\t\t}\n\n\t\tif ($image_type === '')\n\t\t{\n\t\t\t$image_type = $this->image_type;\n\t\t}\n\n\t\tswitch ($image_type)\n\t\t{\n\t\t\tcase 1:\n\t\t\t\tif ( ! function_exists('imagecreatefromgif'))\n\t\t\t\t{\n\t\t\t\t\t$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported'));\n\t\t\t\t\treturn FALSE;\n\t\t\t\t}\n\n\t\t\t\treturn imagecreatefromgif($path);\n\t\t\tcase 2:\n\t\t\t\tif ( ! function_exists('imagecreatefromjpeg'))\n\t\t\t\t{\n\t\t\t\t\t$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported'));\n\t\t\t\t\treturn FALSE;\n\t\t\t\t}\n\n\t\t\t\treturn imagecreatefromjpeg($path);\n\t\t\tcase 3:\n\t\t\t\tif ( ! function_exists('imagecreatefrompng'))\n\t\t\t\t{\n\t\t\t\t\t$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported'));\n\t\t\t\t\treturn FALSE;\n\t\t\t\t}\n\n\t\t\t\treturn imagecreatefrompng($path);\n\t\t\tcase 18:\n\t\t\t\tif ( ! function_exists('imagecreatefromwebp'))\n\t\t\t\t{\n\t\t\t\t\t$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_webp_not_supported'));\n\t\t\t\t\treturn FALSE;\n\t\t\t\t}\n\n\t\t\t\treturn imagecreatefromwebp($path);\n\t\t\tdefault:\n\t\t\t\t$this->set_error(array('imglib_unsupported_imagecreate'));\n\t\t\t\treturn FALSE;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Write image file to disk - GD\n\t *\n\t * Takes an image resource as input and writes the file\n\t * to the specified destination\n\t *\n\t * @param\tresource\n\t * @return\tbool\n\t */\n\tpublic function image_save_gd($resource)\n\t{\n\t\tswitch ($this->image_type)\n\t\t{\n\t\t\tcase 1:\n\t\t\t\tif ( ! function_exists('imagegif'))\n\t\t\t\t{\n\t\t\t\t\t$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported'));\n\t\t\t\t\treturn FALSE;\n\t\t\t\t}\n\n\t\t\t\tif ( ! @imagegif($resource, $this->full_dst_path))\n\t\t\t\t{\n\t\t\t\t\t$this->set_error('imglib_save_failed');\n\t\t\t\t\treturn FALSE;\n\t\t\t\t}\n\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\tif ( ! function_exists('imagejpeg'))\n\t\t\t\t{\n\t\t\t\t\t$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported'));\n\t\t\t\t\treturn FALSE;\n\t\t\t\t}\n\n\t\t\t\tif ( ! @imagejpeg($resource, $this->full_dst_path, $this->quality))\n\t\t\t\t{\n\t\t\t\t\t$this->set_error('imglib_save_failed');\n\t\t\t\t\treturn FALSE;\n\t\t\t\t}\n\t\t\tbreak;\n\t\t\tcase 3:\n\t\t\t\tif ( ! function_exists('imagepng'))\n\t\t\t\t{\n\t\t\t\t\t$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported'));\n\t\t\t\t\treturn FALSE;\n\t\t\t\t}\n\n\t\t\t\tif ( ! @imagepng($resource, $this->full_dst_path))\n\t\t\t\t{\n\t\t\t\t\t$this->set_error('imglib_save_failed');\n\t\t\t\t\treturn FALSE;\n\t\t\t\t}\n\t\t\tbreak;\n\t\t\tcase 18:\n\t\t\t\tif ( ! function_exists('imagewebp'))\n\t\t\t\t{\n\t\t\t\t\t$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_webp_not_supported'));\n\t\t\t\t\treturn FALSE;\n\t\t\t\t}\n\n\t\t\t\tif ( ! @imagewebp($resource, $this->full_dst_path))\n\t\t\t\t{\n\t\t\t\t\t$this->set_error('imglib_save_failed');\n\t\t\t\t\treturn FALSE;\n\t\t\t\t}\n\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t$this->set_error(array('imglib_unsupported_imagecreate'));\n\t\t\t\treturn FALSE;\n\t\t\tbreak;\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Dynamically outputs an image\n\t *\n\t * @param\tresource\n\t * @return\tvoid\n\t */\n\tpublic function image_display_gd($resource)\n\t{\n\t\t// RFC 6266 allows for multibyte filenames, but only in UTF-8,\n\t\t// so we have to make it conditional ...\n\t\t$filename = basename(empty($this->new_image) ? $this->source_image : $this->new_image);\n\t\t$charset = strtoupper(config_item('charset'));\n\t\t$utf8_filename = ($charset !== 'UTF-8')\n\t\t\t? get_instance()->utf8->convert_to_utf8($filename, $charset)\n\t\t\t: $filename;\n\t\tisset($utf8_filename[0]) && $utf8_filename = \" filename*=UTF-8''\".rawurlencode($utf8_filename);\n\n\t\theader('Content-Disposition: filename=\"'.$filename.'\";'.$utf8_filename);\n\t\theader('Content-Type: '.$this->mime_type);\n\t\theader('Content-Transfer-Encoding: binary');\n\t\theader('Last-Modified: '.gmdate('D, d M Y H:i:s', time()).' GMT');\n\n\t\tswitch ($this->image_type)\n\t\t{\n\t\t\tcase 1\t:\timagegif($resource);\n\t\t\t\tbreak;\n\t\t\tcase 2\t:\timagejpeg($resource, NULL, $this->quality);\n\t\t\t\tbreak;\n\t\t\tcase 3\t:\timagepng($resource);\n\t\t\t\tbreak;\n\t\t\tcase 18\t:\timagewebp($resource);\n\t\t\t\tbreak;\n\t\t\tdefault:\techo 'Unable to display the image';\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Re-proportion Image Width/Height\n\t *\n\t * When creating thumbs, the desired width/height\n\t * can end up warping the image due to an incorrect\n\t * ratio between the full-sized image and the thumb.\n\t *\n\t * This function lets us re-proportion the width/height\n\t * if users choose to maintain the aspect ratio when resizing.\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function image_reproportion()\n\t{\n\t\tif (($this->width === 0 && $this->height === 0) OR $this->orig_width === 0 OR $this->orig_height === 0\n\t\t\tOR ( ! ctype_digit((string) $this->width) && ! ctype_digit((string) $this->height))\n\t\t\tOR ! ctype_digit((string) $this->orig_width) OR ! ctype_digit((string) $this->orig_height))\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t// Sanitize\n\t\t$this->width = (int) $this->width;\n\t\t$this->height = (int) $this->height;\n\n\t\tif ($this->master_dim !== 'width' && $this->master_dim !== 'height')\n\t\t{\n\t\t\tif ($this->width > 0 && $this->height > 0)\n\t\t\t{\n\t\t\t\t$this->master_dim = ((($this->orig_height/$this->orig_width) - ($this->height/$this->width)) < 0)\n\t\t\t\t\t\t\t? 'width' : 'height';\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$this->master_dim = ($this->height === 0) ? 'width' : 'height';\n\t\t\t}\n\t\t}\n\t\telseif (($this->master_dim === 'width' && $this->width === 0)\n\t\t\tOR ($this->master_dim === 'height' && $this->height === 0))\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tif ($this->master_dim === 'width')\n\t\t{\n\t\t\t$this->height = (int) ceil($this->width*$this->orig_height/$this->orig_width);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$this->width = (int) ceil($this->orig_width*$this->height/$this->orig_height);\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get image properties\n\t *\n\t * A helper function that gets info about the file\n\t *\n\t * @param\tstring\n\t * @param\tbool\n\t * @return\tmixed\n\t */\n\tpublic function get_image_properties($path = '', $return = FALSE)\n\t{\n\t\t// For now we require GD but we should\n\t\t// find a way to determine this using IM or NetPBM\n\n\t\tif ($path === '')\n\t\t{\n\t\t\t$path = $this->full_src_path;\n\t\t}\n\n\t\tif ( ! file_exists($path))\n\t\t{\n\t\t\t$this->set_error('imglib_invalid_path');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$vals = getimagesize($path);\n\t\tif ($vals === FALSE)\n\t\t{\n\t\t\t$this->set_error('imglib_invalid_image');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png');\n\t\t$mime = isset($types[$vals[2]]) ? 'image/'.$types[$vals[2]] : 'image/jpg';\n\n\t\tif ($return === TRUE)\n\t\t{\n\t\t\treturn array(\n\t\t\t\t'width'      => $vals[0],\n\t\t\t\t'height'     => $vals[1],\n\t\t\t\t'image_type' => $vals[2],\n\t\t\t\t'size_str'   => $vals[3],\n\t\t\t\t'mime_type'  => $mime\n\t\t\t);\n\t\t}\n\n\t\t$this->orig_width  = $vals[0];\n\t\t$this->orig_height = $vals[1];\n\t\t$this->image_type  = $vals[2];\n\t\t$this->size_str    = $vals[3];\n\t\t$this->mime_type   = $mime;\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Size calculator\n\t *\n\t * This function takes a known width x height and\n\t * recalculates it to a new size. Only one\n\t * new variable needs to be known\n\t *\n\t *\t$props = array(\n\t *\t\t\t'width'\t\t=> $width,\n\t *\t\t\t'height'\t=> $height,\n\t *\t\t\t'new_width'\t=> 40,\n\t *\t\t\t'new_height'\t=> ''\n\t *\t\t);\n\t *\n\t * @param\tarray\n\t * @return\tarray\n\t */\n\tpublic function size_calculator($vals)\n\t{\n\t\tif ( ! is_array($vals))\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t$allowed = array('new_width', 'new_height', 'width', 'height');\n\n\t\tforeach ($allowed as $item)\n\t\t{\n\t\t\tif (empty($vals[$item]))\n\t\t\t{\n\t\t\t\t$vals[$item] = 0;\n\t\t\t}\n\t\t}\n\n\t\tif ($vals['width'] === 0 OR $vals['height'] === 0)\n\t\t{\n\t\t\treturn $vals;\n\t\t}\n\n\t\tif ($vals['new_width'] === 0)\n\t\t{\n\t\t\t$vals['new_width'] = ceil($vals['width']*$vals['new_height']/$vals['height']);\n\t\t}\n\t\telseif ($vals['new_height'] === 0)\n\t\t{\n\t\t\t$vals['new_height'] = ceil($vals['new_width']*$vals['height']/$vals['width']);\n\t\t}\n\n\t\treturn $vals;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Explode source_image\n\t *\n\t * This is a helper function that extracts the extension\n\t * from the source_image.  This function lets us deal with\n\t * source_images with multiple periods, like: my.cool.jpg\n\t * It returns an associative array with two elements:\n\t * $array['ext']  = '.jpg';\n\t * $array['name'] = 'my.cool';\n\t *\n\t * @param\tarray\n\t * @return\tarray\n\t */\n\tpublic function explode_name($source_image)\n\t{\n\t\t$ext = strrchr($source_image, '.');\n\t\t$name = ($ext === FALSE) ? $source_image : substr($source_image, 0, -strlen($ext));\n\n\t\treturn array('ext' => $ext, 'name' => $name);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Is GD Installed?\n\t *\n\t * @return\tbool\n\t */\n\tpublic function gd_loaded()\n\t{\n\t\tif ( ! extension_loaded('gd'))\n\t\t{\n\t\t\t/* As it is stated in the PHP manual, dl() is not always available\n\t\t\t * and even if so - it could generate an E_WARNING message on failure\n\t\t\t */\n\t\t\treturn (function_exists('dl') && @dl('gd.so'));\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get GD version\n\t *\n\t * @return\tmixed\n\t */\n\tpublic function gd_version()\n\t{\n\t\tif (function_exists('gd_info'))\n\t\t{\n\t\t\t$gd_version = @gd_info();\n\t\t\treturn preg_replace('/\\D/', '', $gd_version['GD Version']);\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set error message\n\t *\n\t * @param\tstring\n\t * @return\tvoid\n\t */\n\tpublic function set_error($msg)\n\t{\n\t\t$CI =& get_instance();\n\t\t$CI->lang->load('imglib');\n\n\t\tif (is_array($msg))\n\t\t{\n\t\t\tforeach ($msg as $val)\n\t\t\t{\n\t\t\t\t$msg = ($CI->lang->line($val) === FALSE) ? $val : $CI->lang->line($val);\n\t\t\t\t$this->error_msg[] = $msg;\n\t\t\t\tlog_message('error', $msg);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$msg = ($CI->lang->line($msg) === FALSE) ? $msg : $CI->lang->line($msg);\n\t\t\t$this->error_msg[] = $msg;\n\t\t\tlog_message('error', $msg);\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show error messages\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tpublic function display_errors($open = '<p>', $close = '</p>')\n\t{\n\t\treturn (count($this->error_msg) > 0) ? $open.implode($close.$open, $this->error_msg).$close : '';\n\t}\n\n}\n"
  },
  {
    "path": "system/libraries/Migration.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Migration Class\n *\n * All migrations should implement this, forces up() and down() and gives\n * access to the CI super-global.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tLibraries\n * @author\t\tReactor Engineers\n * @link\n */\nclass CI_Migration {\n\n\t/**\n\t * Whether the library is enabled\n\t *\n\t * @var bool\n\t */\n\tprotected $_migration_enabled = FALSE;\n\n\t/**\n\t * Migration numbering type\n\t *\n\t * @var\tbool\n\t */\n\tprotected $_migration_type = 'sequential';\n\n\t/**\n\t * Path to migration classes\n\t *\n\t * @var string\n\t */\n\tprotected $_migration_path = NULL;\n\n\t/**\n\t * Current migration version\n\t *\n\t * @var mixed\n\t */\n\tprotected $_migration_version = 0;\n\n\t/**\n\t * Database table with migration info\n\t *\n\t * @var string\n\t */\n\tprotected $_migration_table = 'migrations';\n\n\t/**\n\t * Whether to automatically run migrations\n\t *\n\t * @var\tbool\n\t */\n\tprotected $_migration_auto_latest = FALSE;\n\n\t/**\n\t * Migration basename regex\n\t *\n\t * @var string\n\t */\n\tprotected $_migration_regex;\n\n\t/**\n\t * Error message\n\t *\n\t * @var string\n\t */\n\tprotected $_error_string = '';\n\n\t/**\n\t * Initialize Migration Class\n\t *\n\t * @param\tarray\t$config\n\t * @return\tvoid\n\t */\n\tpublic function __construct($config = array())\n\t{\n\t\t// Only run this constructor on main library load\n\t\tif ( ! in_array(get_class($this), array('CI_Migration', config_item('subclass_prefix').'Migration'), TRUE))\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tforeach ($config as $key => $val)\n\t\t{\n\t\t\t$this->{'_'.$key} = $val;\n\t\t}\n\n\t\tlog_message('info', 'Migrations Class Initialized');\n\n\t\t// Are they trying to use migrations while it is disabled?\n\t\tif ($this->_migration_enabled !== TRUE)\n\t\t{\n\t\t\tshow_error('Migrations has been loaded but is disabled or set up incorrectly.');\n\t\t}\n\n\t\t// If not set, set it\n\t\t$this->_migration_path !== '' OR $this->_migration_path = APPPATH.'migrations/';\n\n\t\t// Add trailing slash if not set\n\t\t$this->_migration_path = rtrim($this->_migration_path, '/').'/';\n\n\t\t// Load migration language\n\t\t$this->lang->load('migration');\n\n\t\t// They'll probably be using dbforge\n\t\t$this->load->dbforge();\n\n\t\t// Make sure the migration table name was set.\n\t\tif (empty($this->_migration_table))\n\t\t{\n\t\t\tshow_error('Migrations configuration file (migration.php) must have \"migration_table\" set.');\n\t\t}\n\n\t\t// Migration basename regex\n\t\t$this->_migration_regex = ($this->_migration_type === 'timestamp')\n\t\t\t? '/^\\d{14}_(\\w+)$/'\n\t\t\t: '/^\\d{3}_(\\w+)$/';\n\n\t\t// Make sure a valid migration numbering type was set.\n\t\tif ( ! in_array($this->_migration_type, array('sequential', 'timestamp')))\n\t\t{\n\t\t\tshow_error('An invalid migration numbering type was specified: '.$this->_migration_type);\n\t\t}\n\n\t\t// If the migrations table is missing, make it\n\t\tif ( ! $this->db->table_exists($this->_migration_table))\n\t\t{\n\t\t\t$this->dbforge->add_field(array(\n\t\t\t\t'version' => array('type' => 'BIGINT', 'constraint' => 20),\n\t\t\t));\n\n\t\t\t$this->dbforge->create_table($this->_migration_table, TRUE);\n\n\t\t\t$this->db->insert($this->_migration_table, array('version' => 0));\n\t\t}\n\n\t\t// Do we auto migrate to the latest migration?\n\t\tif ($this->_migration_auto_latest === TRUE && ! $this->latest())\n\t\t{\n\t\t\tshow_error($this->error_string());\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Migrate to a schema version\n\t *\n\t * Calls each migration step required to get to the schema version of\n\t * choice\n\t *\n\t * @param\tstring\t$target_version\tTarget schema version\n\t * @return\tmixed\tTRUE if no migrations are found, current version string on success, FALSE on failure\n\t */\n\tpublic function version($target_version)\n\t{\n\t\t// Note: We use strings, so that timestamp versions work on 32-bit systems\n\t\t$current_version = $this->_get_version();\n\n\t\tif ($this->_migration_type === 'sequential')\n\t\t{\n\t\t\t$target_version = sprintf('%03d', $target_version);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$target_version = (string) $target_version;\n\t\t}\n\n\t\t$migrations = $this->find_migrations();\n\n\t\tif ($target_version > 0 && ! isset($migrations[$target_version]))\n\t\t{\n\t\t\t$this->_error_string = sprintf($this->lang->line('migration_not_found'), $target_version);\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif ($target_version > $current_version)\n\t\t{\n\t\t\t$method = 'up';\n\t\t}\n\t\telseif ($target_version < $current_version)\n\t\t{\n\t\t\t$method = 'down';\n\t\t\t// We need this so that migrations are applied in reverse order\n\t\t\tkrsort($migrations);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Well, there's nothing to migrate then ...\n\t\t\treturn TRUE;\n\t\t}\n\n\t\t// Validate all available migrations within our target range.\n\t\t//\n\t\t// Unfortunately, we'll have to use another loop to run them\n\t\t// in order to avoid leaving the procedure in a broken state.\n\t\t//\n\t\t// See https://github.com/bcit-ci/CodeIgniter/issues/4539\n\t\t$pending = array();\n\t\tforeach ($migrations as $number => $file)\n\t\t{\n\t\t\t// Ignore versions out of our range.\n\t\t\t//\n\t\t\t// Because we've previously sorted the $migrations array depending on the direction,\n\t\t\t// we can safely break the loop once we reach $target_version ...\n\t\t\tif ($method === 'up')\n\t\t\t{\n\t\t\t\tif ($number <= $current_version)\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\telseif ($number > $target_version)\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif ($number > $current_version)\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\telseif ($number <= $target_version)\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check for sequence gaps\n\t\t\tif ($this->_migration_type === 'sequential')\n\t\t\t{\n\t\t\t\tif (isset($previous) && abs($number - $previous) > 1)\n\t\t\t\t{\n\t\t\t\t\t$this->_error_string = sprintf($this->lang->line('migration_sequence_gap'), $number);\n\t\t\t\t\treturn FALSE;\n\t\t\t\t}\n\n\t\t\t\t$previous = $number;\n\t\t\t}\n\n\t\t\tinclude_once($file);\n\t\t\t$class = 'Migration_'.ucfirst(strtolower($this->_get_migration_name(basename($file, '.php'))));\n\n\t\t\t// Validate the migration file structure\n\t\t\tif ( ! class_exists($class, FALSE))\n\t\t\t{\n\t\t\t\t$this->_error_string = sprintf($this->lang->line('migration_class_doesnt_exist'), $class);\n\t\t\t\treturn FALSE;\n\t\t\t}\n\t\t\telseif ( ! method_exists($class, $method) OR ! (new ReflectionMethod($class, $method))->isPublic())\n\t\t\t{\n\t\t\t\t$this->_error_string = sprintf($this->lang->line('migration_missing_'.$method.'_method'), $class);\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\t$pending[$number] = array($class, $method);\n\t\t}\n\n\t\t// Now just run the necessary migrations\n\t\tforeach ($pending as $number => $migration)\n\t\t{\n\t\t\tlog_message('debug', 'Migrating '.$method.' from version '.$current_version.' to version '.$number);\n\n\t\t\t$migration[0] = new $migration[0];\n\t\t\tcall_user_func($migration);\n\t\t\t$current_version = $number;\n\t\t\t$this->_update_version($current_version);\n\t\t}\n\n\t\t// This is necessary when moving down, since the the last migration applied\n\t\t// will be the down() method for the next migration up from the target\n\t\tif ($current_version <> $target_version)\n\t\t{\n\t\t\t$current_version = $target_version;\n\t\t\t$this->_update_version($current_version);\n\t\t}\n\n\t\tlog_message('debug', 'Finished migrating to '.$current_version);\n\t\treturn $current_version;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Sets the schema to the latest migration\n\t *\n\t * @return\tmixed\tCurrent version string on success, FALSE on failure\n\t */\n\tpublic function latest()\n\t{\n\t\t$migrations = $this->find_migrations();\n\n\t\tif (empty($migrations))\n\t\t{\n\t\t\t$this->_error_string = $this->lang->line('migration_none_found');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$last_migration = basename(end($migrations));\n\n\t\t// Calculate the last migration step from existing migration\n\t\t// filenames and proceed to the standard version migration\n\t\treturn $this->version($this->_get_migration_number($last_migration));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Sets the schema to the migration version set in config\n\t *\n\t * @return\tmixed\tTRUE if no migrations are found, current version string on success, FALSE on failure\n\t */\n\tpublic function current()\n\t{\n\t\treturn $this->version($this->_migration_version);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Error string\n\t *\n\t * @return\tstring\tError message returned as a string\n\t */\n\tpublic function error_string()\n\t{\n\t\treturn $this->_error_string;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Retrieves list of available migration scripts\n\t *\n\t * @return\tarray\tlist of migration file paths sorted by version\n\t */\n\tpublic function find_migrations()\n\t{\n\t\t$migrations = array();\n\n\t\t// Load all *_*.php files in the migrations path\n\t\tforeach (glob($this->_migration_path.'*_*.php') as $file)\n\t\t{\n\t\t\t$name = basename($file, '.php');\n\n\t\t\t// Filter out non-migration files\n\t\t\tif (preg_match($this->_migration_regex, $name))\n\t\t\t{\n\t\t\t\t$number = $this->_get_migration_number($name);\n\n\t\t\t\t// There cannot be duplicate migration numbers\n\t\t\t\tif (isset($migrations[$number]))\n\t\t\t\t{\n\t\t\t\t\t$this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $number);\n\t\t\t\t\tshow_error($this->_error_string);\n\t\t\t\t}\n\n\t\t\t\t$migrations[$number] = $file;\n\t\t\t}\n\t\t}\n\n\t\tksort($migrations);\n\t\treturn $migrations;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Extracts the migration number from a filename\n\t *\n\t * @param\tstring\t$migration\n\t * @return\tstring\tNumeric portion of a migration filename\n\t */\n\tprotected function _get_migration_number($migration)\n\t{\n\t\treturn sscanf($migration, '%[0-9]+', $number)\n\t\t\t? $number : '0';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Extracts the migration class name from a filename\n\t *\n\t * @param\tstring\t$migration\n\t * @return\tstring\ttext portion of a migration filename\n\t */\n\tprotected function _get_migration_name($migration)\n\t{\n\t\t$parts = explode('_', $migration);\n\t\tarray_shift($parts);\n\t\treturn implode('_', $parts);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Retrieves current schema version\n\t *\n\t * @return\tstring\tCurrent migration version\n\t */\n\tprotected function _get_version()\n\t{\n\t\t$row = $this->db->select('version')->get($this->_migration_table)->row();\n\t\treturn $row ? $row->version : '0';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Stores the current schema version\n\t *\n\t * @param\tstring\t$migration\tMigration reached\n\t * @return\tvoid\n\t */\n\tprotected function _update_version($migration)\n\t{\n\t\t$this->db->update($this->_migration_table, array(\n\t\t\t'version' => $migration\n\t\t));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Enable the use of CI super-global\n\t *\n\t * @param\tstring\t$var\n\t * @return\tmixed\n\t */\n\tpublic function __get($var)\n\t{\n\t\treturn get_instance()->$var;\n\t}\n\n}\n"
  },
  {
    "path": "system/libraries/Pagination.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Pagination Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tPagination\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/pagination.html\n */\nclass CI_Pagination {\n\n\t/**\n\t * Base URL\n\t *\n\t * The page that we're linking to\n\t *\n\t * @var\tstring\n\t */\n\tprotected $base_url\t\t= '';\n\n\t/**\n\t * Prefix\n\t *\n\t * @var\tstring\n\t */\n\tprotected $prefix = '';\n\n\t/**\n\t * Suffix\n\t *\n\t * @var\tstring\n\t */\n\tprotected $suffix = '';\n\n\t/**\n\t * Total number of items\n\t *\n\t * @var\tint\n\t */\n\tprotected $total_rows = 0;\n\n\t/**\n\t * Number of links to show\n\t *\n\t * Relates to \"digit\" type links shown before/after\n\t * the currently viewed page.\n\t *\n\t * @var\tint\n\t */\n\tprotected $num_links = 2;\n\n\t/**\n\t * Items per page\n\t *\n\t * @var\tint\n\t */\n\tpublic $per_page = 10;\n\n\t/**\n\t * Current page\n\t *\n\t * @var\tint\n\t */\n\tpublic $cur_page = 0;\n\n\t/**\n\t * Use page numbers flag\n\t *\n\t * Whether to use actual page numbers instead of an offset\n\t *\n\t * @var\tbool\n\t */\n\tprotected $use_page_numbers = FALSE;\n\n\t/**\n\t * First link\n\t *\n\t * @var\tstring\n\t */\n\tprotected $first_link = '&lsaquo; First';\n\n\t/**\n\t * Next link\n\t *\n\t * @var\tstring\n\t */\n\tprotected $next_link = '&gt;';\n\n\t/**\n\t * Previous link\n\t *\n\t * @var\tstring\n\t */\n\tprotected $prev_link = '&lt;';\n\n\t/**\n\t * Last link\n\t *\n\t * @var\tstring\n\t */\n\tprotected $last_link = 'Last &rsaquo;';\n\n\t/**\n\t * URI Segment\n\t *\n\t * @var\tint\n\t */\n\tprotected $uri_segment = 0;\n\n\t/**\n\t * Full tag open\n\t *\n\t * @var\tstring\n\t */\n\tprotected $full_tag_open = '';\n\n\t/**\n\t * Full tag close\n\t *\n\t * @var\tstring\n\t */\n\tprotected $full_tag_close = '';\n\n\t/**\n\t * First tag open\n\t *\n\t * @var\tstring\n\t */\n\tprotected $first_tag_open = '';\n\n\t/**\n\t * First tag close\n\t *\n\t * @var\tstring\n\t */\n\tprotected $first_tag_close = '';\n\n\t/**\n\t * Last tag open\n\t *\n\t * @var\tstring\n\t */\n\tprotected $last_tag_open = '';\n\n\t/**\n\t * Last tag close\n\t *\n\t * @var\tstring\n\t */\n\tprotected $last_tag_close = '';\n\n\t/**\n\t * First URL\n\t *\n\t * An alternative URL for the first page\n\t *\n\t * @var\tstring\n\t */\n\tprotected $first_url = '';\n\n\t/**\n\t * Current tag open\n\t *\n\t * @var\tstring\n\t */\n\tprotected $cur_tag_open = '<strong>';\n\n\t/**\n\t * Current tag close\n\t *\n\t * @var\tstring\n\t */\n\tprotected $cur_tag_close = '</strong>';\n\n\t/**\n\t * Next tag open\n\t *\n\t * @var\tstring\n\t */\n\tprotected $next_tag_open = '';\n\n\t/**\n\t * Next tag close\n\t *\n\t * @var\tstring\n\t */\n\tprotected $next_tag_close = '';\n\n\t/**\n\t * Previous tag open\n\t *\n\t * @var\tstring\n\t */\n\tprotected $prev_tag_open = '';\n\n\t/**\n\t * Previous tag close\n\t *\n\t * @var\tstring\n\t */\n\tprotected $prev_tag_close = '';\n\n\t/**\n\t * Number tag open\n\t *\n\t * @var\tstring\n\t */\n\tprotected $num_tag_open = '';\n\n\t/**\n\t * Number tag close\n\t *\n\t * @var\tstring\n\t */\n\tprotected $num_tag_close = '';\n\n\t/**\n\t * Page query string flag\n\t *\n\t * @var\tbool\n\t */\n\tprotected $page_query_string = FALSE;\n\n\t/**\n\t * Query string segment\n\t *\n\t * @var\tstring\n\t */\n\tprotected $query_string_segment = 'per_page';\n\n\t/**\n\t * Display pages flag\n\t *\n\t * @var\tbool\n\t */\n\tprotected $display_pages = TRUE;\n\n\t/**\n\t * Attributes\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_attributes = '';\n\n\t/**\n\t * Link types\n\t *\n\t * \"rel\" attribute\n\t *\n\t * @see\tCI_Pagination::_attr_rel()\n\t * @var\tarray\n\t */\n\tprotected $_link_types = array();\n\n\t/**\n\t * Reuse query string flag\n\t *\n\t * @var\tbool\n\t */\n\tprotected $reuse_query_string = FALSE;\n\n\t/**\n\t * Use global URL suffix flag\n\t *\n\t * @var\tbool\n\t */\n\tprotected $use_global_url_suffix = FALSE;\n\n\t/**\n\t * Data page attribute\n\t *\n\t * @var\tstring\n\t */\n\tprotected $data_page_attr = 'data-ci-pagination-page';\n\n\t/**\n\t * CI Singleton\n\t *\n\t * @var\tobject\n\t */\n\tprotected $CI;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Constructor\n\t *\n\t * @param\tarray\t$params\tInitialization parameters\n\t * @return\tvoid\n\t */\n\tpublic function __construct($params = array())\n\t{\n\t\t$this->CI =& get_instance();\n\t\t$this->CI->load->language('pagination');\n\t\tforeach (array('first_link', 'next_link', 'prev_link', 'last_link') as $key)\n\t\t{\n\t\t\tif (($val = $this->CI->lang->line('pagination_'.$key)) !== FALSE)\n\t\t\t{\n\t\t\t\t$this->$key = $val;\n\t\t\t}\n\t\t}\n\n\t\t// _parse_attributes(), called by initialize(), needs to run at least once\n\t\t// in order to enable \"rel\" attributes, and this triggers it.\n\t\tisset($params['attributes']) OR $params['attributes'] = array();\n\n\t\t$this->initialize($params);\n\t\tlog_message('info', 'Pagination Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Initialize Preferences\n\t *\n\t * @param\tarray\t$params\tInitialization parameters\n\t * @return\tCI_Pagination\n\t */\n\tpublic function initialize(array $params = array())\n\t{\n\t\tif (isset($params['attributes']) && is_array($params['attributes']))\n\t\t{\n\t\t\t$this->_parse_attributes($params['attributes']);\n\t\t\tunset($params['attributes']);\n\t\t}\n\n\t\tforeach ($params as $key => $val)\n\t\t{\n\t\t\tif (property_exists($this, $key))\n\t\t\t{\n\t\t\t\t$this->$key = $val;\n\t\t\t}\n\t\t}\n\n\t\tif ($this->CI->config->item('enable_query_strings') === TRUE)\n\t\t{\n\t\t\t$this->page_query_string = TRUE;\n\t\t}\n\n\t\tif ($this->use_global_url_suffix === TRUE)\n\t\t{\n\t\t\t$this->suffix = $this->CI->config->item('url_suffix');\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Generate the pagination links\n\t *\n\t * @return\tstring\n\t */\n\tpublic function create_links()\n\t{\n\t\t// If our item count or per-page total is zero there is no need to continue.\n\t\t// Note: DO NOT change the operator to === here!\n\t\tif ($this->total_rows == 0 OR $this->per_page == 0)\n\t\t{\n\t\t\treturn '';\n\t\t}\n\n\t\t// Calculate the total number of pages\n\t\t$num_pages = (int) ceil($this->total_rows / $this->per_page);\n\n\t\t// Is there only one page? Hm... nothing more to do here then.\n\t\tif ($num_pages === 1)\n\t\t{\n\t\t\treturn '';\n\t\t}\n\n\t\t// Check the user defined number of links.\n\t\t$this->num_links = (int) $this->num_links;\n\n\t\tif ($this->num_links < 0)\n\t\t{\n\t\t\tshow_error('Your number of links must be a non-negative number.');\n\t\t}\n\n\t\t// Keep any existing query string items.\n\t\t// Note: Has nothing to do with any other query string option.\n\t\tif ($this->reuse_query_string === TRUE)\n\t\t{\n\t\t\t$get = $this->CI->input->get();\n\n\t\t\t// Unset the control, method, old-school routing options\n\t\t\tunset($get['c'], $get['m'], $get[$this->query_string_segment]);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$get = array();\n\t\t}\n\n\t\t// Put together our base and first URLs.\n\t\t// Note: DO NOT append to the properties as that would break successive calls\n\t\t$base_url = trim($this->base_url);\n\t\t$first_url = $this->first_url;\n\n\t\t$query_string = '';\n\t\t$query_string_sep = (strpos($base_url, '?') === FALSE) ? '?' : '&amp;';\n\n\t\t// Are we using query strings?\n\t\tif ($this->page_query_string === TRUE)\n\t\t{\n\t\t\t// If a custom first_url hasn't been specified, we'll create one from\n\t\t\t// the base_url, but without the page item.\n\t\t\tif ($first_url === '')\n\t\t\t{\n\t\t\t\t$first_url = $base_url;\n\n\t\t\t\t// If we saved any GET items earlier, make sure they're appended.\n\t\t\t\tif ( ! empty($get))\n\t\t\t\t{\n\t\t\t\t\t$first_url .= $query_string_sep.http_build_query($get);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Add the page segment to the end of the query string, where the\n\t\t\t// page number will be appended.\n\t\t\t$base_url .= $query_string_sep.http_build_query(array_merge($get, array($this->query_string_segment => '')));\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Standard segment mode.\n\t\t\t// Generate our saved query string to append later after the page number.\n\t\t\tif ( ! empty($get))\n\t\t\t{\n\t\t\t\t$query_string = $query_string_sep.http_build_query($get);\n\t\t\t\t$this->suffix .= $query_string;\n\t\t\t}\n\n\t\t\t// Does the base_url have the query string in it?\n\t\t\t// If we're supposed to save it, remove it so we can append it later.\n\t\t\tif ($this->reuse_query_string === TRUE && ($base_query_pos = strpos($base_url, '?')) !== FALSE)\n\t\t\t{\n\t\t\t\t$base_url = substr($base_url, 0, $base_query_pos);\n\t\t\t}\n\n\t\t\tif ($first_url === '')\n\t\t\t{\n\t\t\t\t$first_url = $base_url.$query_string;\n\t\t\t}\n\n\t\t\t$base_url = rtrim($base_url, '/').'/';\n\t\t}\n\n\t\t// Determine the current page number.\n\t\t$base_page = ($this->use_page_numbers) ? 1 : 0;\n\n\t\t// Are we using query strings?\n\t\tif ($this->page_query_string === TRUE)\n\t\t{\n\t\t\t$this->cur_page = $this->CI->input->get($this->query_string_segment);\n\t\t}\n\t\telseif (empty($this->cur_page))\n\t\t{\n\t\t\t// Default to the last segment number if one hasn't been defined.\n\t\t\tif ($this->uri_segment === 0)\n\t\t\t{\n\t\t\t\t$this->uri_segment = count($this->CI->uri->segment_array());\n\t\t\t}\n\n\t\t\t$this->cur_page = $this->CI->uri->segment($this->uri_segment);\n\n\t\t\t// Remove any specified prefix/suffix from the segment.\n\t\t\tif ($this->prefix !== '' OR $this->suffix !== '')\n\t\t\t{\n\t\t\t\t$this->cur_page = str_replace(array($this->prefix, $this->suffix), '', $this->cur_page);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$this->cur_page = (string) $this->cur_page;\n\t\t}\n\n\t\t// If something isn't quite right, back to the default base page.\n\t\tif ( ! ctype_digit((string) $this->cur_page) OR ($this->use_page_numbers && (int) $this->cur_page === 0))\n\t\t{\n\t\t\t$this->cur_page = $base_page;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Make sure we're using integers for comparisons later.\n\t\t\t$this->cur_page = (int) $this->cur_page;\n\t\t}\n\n\t\t// Is the page number beyond the result range?\n\t\t// If so, we show the last page.\n\t\tif ($this->use_page_numbers)\n\t\t{\n\t\t\tif ($this->cur_page > $num_pages)\n\t\t\t{\n\t\t\t\t$this->cur_page = $num_pages;\n\t\t\t}\n\t\t}\n\t\telseif ($this->cur_page > $this->total_rows)\n\t\t{\n\t\t\t$this->cur_page = ($num_pages - 1) * $this->per_page;\n\t\t}\n\n\t\t$uri_page_number = $this->cur_page;\n\n\t\t// If we're using offset instead of page numbers, convert it\n\t\t// to a page number, so we can generate the surrounding number links.\n\t\tif ( ! $this->use_page_numbers)\n\t\t{\n\t\t\t$this->cur_page = (int) floor(($this->cur_page/$this->per_page) + 1);\n\t\t}\n\n\t\t// Calculate the start and end numbers. These determine\n\t\t// which number to start and end the digit links with.\n\t\t$start\t= (($this->cur_page - $this->num_links) > 0) ? $this->cur_page - ($this->num_links - 1) : 1;\n\t\t$end\t= (($this->cur_page + $this->num_links) < $num_pages) ? $this->cur_page + $this->num_links : $num_pages;\n\n\t\t// And here we go...\n\t\t$output = '';\n\n\t\t// Render the \"First\" link.\n\t\tif ($this->first_link !== FALSE && $this->cur_page > ($this->num_links + 1 + ! $this->num_links))\n\t\t{\n\t\t\t// Take the general parameters, and squeeze this pagination-page attr in for JS frameworks.\n\t\t\t$attributes = sprintf('%s %s=\"%d\"', $this->_attributes, $this->data_page_attr, 1);\n\n\t\t\t$output .= $this->first_tag_open.'<a href=\"'.$first_url.'\"'.$attributes.$this->_attr_rel('start').'>'\n\t\t\t\t.$this->first_link.'</a>'.$this->first_tag_close;\n\t\t}\n\n\t\t// Render the \"Previous\" link.\n\t\tif ($this->prev_link !== FALSE && $this->cur_page !== 1)\n\t\t{\n\t\t\t$i = ($this->use_page_numbers) ? $uri_page_number - 1 : $uri_page_number - $this->per_page;\n\n\t\t\t$attributes = sprintf('%s %s=\"%d\"', $this->_attributes, $this->data_page_attr, ($this->cur_page - 1));\n\n\t\t\tif ($i === $base_page)\n\t\t\t{\n\t\t\t\t// First page\n\t\t\t\t$output .= $this->prev_tag_open.'<a href=\"'.$first_url.'\"'.$attributes.$this->_attr_rel('prev').'>'\n\t\t\t\t\t.$this->prev_link.'</a>'.$this->prev_tag_close;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$append = $this->prefix.$i.$this->suffix;\n\t\t\t\t$output .= $this->prev_tag_open.'<a href=\"'.$base_url.$append.'\"'.$attributes.$this->_attr_rel('prev').'>'\n\t\t\t\t\t.$this->prev_link.'</a>'.$this->prev_tag_close;\n\t\t\t}\n\n\t\t}\n\n\t\t// Render the pages\n\t\tif ($this->display_pages !== FALSE)\n\t\t{\n\t\t\t// Write the digit links\n\t\t\tfor ($loop = $start - 1; $loop <= $end; $loop++)\n\t\t\t{\n\t\t\t\t$i = ($this->use_page_numbers) ? $loop : ($loop * $this->per_page) - $this->per_page;\n\n\t\t\t\t$attributes = sprintf('%s %s=\"%d\"', $this->_attributes, $this->data_page_attr, $loop);\n\n\t\t\t\tif ($i >= $base_page)\n\t\t\t\t{\n\t\t\t\t\tif ($this->cur_page === $loop)\n\t\t\t\t\t{\n\t\t\t\t\t\t// Current page\n\t\t\t\t\t\t$output .= $this->cur_tag_open.$loop.$this->cur_tag_close;\n\t\t\t\t\t}\n\t\t\t\t\telseif ($i === $base_page)\n\t\t\t\t\t{\n\t\t\t\t\t\t// First page\n\t\t\t\t\t\t$output .= $this->num_tag_open.'<a href=\"'.$first_url.'\"'.$attributes.$this->_attr_rel('start').'>'\n\t\t\t\t\t\t\t.$loop.'</a>'.$this->num_tag_close;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t$append = $this->prefix.$i.$this->suffix;\n\t\t\t\t\t\t$output .= $this->num_tag_open.'<a href=\"'.$base_url.$append.'\"'.$attributes.'>'\n\t\t\t\t\t\t\t.$loop.'</a>'.$this->num_tag_close;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Render the \"next\" link\n\t\tif ($this->next_link !== FALSE && $this->cur_page < $num_pages)\n\t\t{\n\t\t\t$i = ($this->use_page_numbers) ? $this->cur_page + 1 : $this->cur_page * $this->per_page;\n\n\t\t\t$attributes = sprintf('%s %s=\"%d\"', $this->_attributes, $this->data_page_attr, $this->cur_page + 1);\n\n\t\t\t$output .= $this->next_tag_open.'<a href=\"'.$base_url.$this->prefix.$i.$this->suffix.'\"'.$attributes\n\t\t\t\t.$this->_attr_rel('next').'>'.$this->next_link.'</a>'.$this->next_tag_close;\n\t\t}\n\n\t\t// Render the \"Last\" link\n\t\tif ($this->last_link !== FALSE && ($this->cur_page + $this->num_links + ! $this->num_links) < $num_pages)\n\t\t{\n\t\t\t$i = ($this->use_page_numbers) ? $num_pages : ($num_pages * $this->per_page) - $this->per_page;\n\n\t\t\t$attributes = sprintf('%s %s=\"%d\"', $this->_attributes, $this->data_page_attr, $num_pages);\n\n\t\t\t$output .= $this->last_tag_open.'<a href=\"'.$base_url.$this->prefix.$i.$this->suffix.'\"'.$attributes.'>'\n\t\t\t\t.$this->last_link.'</a>'.$this->last_tag_close;\n\t\t}\n\n\t\t// Kill double slashes. Note: Sometimes we can end up with a double slash\n\t\t// in the penultimate link so we'll kill all double slashes.\n\t\t$output = preg_replace('#([^:\"])//+#', '\\\\1/', $output);\n\n\t\t// Add the wrapper HTML if exists\n\t\treturn $this->full_tag_open.$output.$this->full_tag_close;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Parse attributes\n\t *\n\t * @param\tarray\t$attributes\n\t * @return\tvoid\n\t */\n\tprotected function _parse_attributes($attributes)\n\t{\n\t\tisset($attributes['rel']) OR $attributes['rel'] = TRUE;\n\t\t$this->_link_types = ($attributes['rel'])\n\t\t\t? array('start' => 'start', 'prev' => 'prev', 'next' => 'next')\n\t\t\t: array();\n\t\tunset($attributes['rel']);\n\n\t\t$this->_attributes = '';\n\t\tforeach ($attributes as $key => $value)\n\t\t{\n\t\t\t$this->_attributes .= ' '.$key.'=\"'.$value.'\"';\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Add \"rel\" attribute\n\t *\n\t * @link\thttps://www.w3.org/TR/html5/links.html#linkTypes\n\t * @param\tstring\t$type\n\t * @return\tstring\n\t */\n\tprotected function _attr_rel($type)\n\t{\n\t\tif (isset($this->_link_types[$type]))\n\t\t{\n\t\t\tunset($this->_link_types[$type]);\n\t\t\treturn ' rel=\"'.$type.'\"';\n\t\t}\n\n\t\treturn '';\n\t}\n\n}\n"
  },
  {
    "path": "system/libraries/Parser.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Parser Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tParser\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/parser.html\n */\nclass CI_Parser {\n\n\t/**\n\t * Left delimiter character for pseudo vars\n\t *\n\t * @var string\n\t */\n\tpublic $l_delim = '{';\n\n\t/**\n\t * Right delimiter character for pseudo vars\n\t *\n\t * @var string\n\t */\n\tpublic $r_delim = '}';\n\n\t/**\n\t * Reference to CodeIgniter instance\n\t *\n\t * @var object\n\t */\n\tprotected $CI;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __construct()\n\t{\n\t\t$this->CI =& get_instance();\n\t\tlog_message('info', 'Parser Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Parse a template\n\t *\n\t * Parses pseudo-variables contained in the specified template view,\n\t * replacing them with the data in the second param\n\t *\n\t * @param\tstring\n\t * @param\tarray\n\t * @param\tbool\n\t * @return\tstring\n\t */\n\tpublic function parse($template, $data, $return = FALSE)\n\t{\n\t\t$template = $this->CI->load->view($template, $data, TRUE);\n\n\t\treturn $this->_parse($template, $data, $return);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Parse a String\n\t *\n\t * Parses pseudo-variables contained in the specified string,\n\t * replacing them with the data in the second param\n\t *\n\t * @param\tstring\n\t * @param\tarray\n\t * @param\tbool\n\t * @return\tstring\n\t */\n\tpublic function parse_string($template, $data, $return = FALSE)\n\t{\n\t\treturn $this->_parse($template, $data, $return);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Parse a template\n\t *\n\t * Parses pseudo-variables contained in the specified template,\n\t * replacing them with the data in the second param\n\t *\n\t * @param\tstring\n\t * @param\tarray\n\t * @param\tbool\n\t * @return\tstring\n\t */\n\tprotected function _parse($template, $data, $return = FALSE)\n\t{\n\t\tif ($template === '')\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$replace = array();\n\t\tforeach ($data as $key => $val)\n\t\t{\n\t\t\t$replace = array_merge(\n\t\t\t\t$replace,\n\t\t\t\tis_array($val)\n\t\t\t\t\t? $this->_parse_pair($key, $val, $template)\n\t\t\t\t\t: $this->_parse_single($key, (string) $val, $template)\n\t\t\t);\n\t\t}\n\n\t\tunset($data);\n\t\t$template = strtr($template, $replace);\n\n\t\tif ($return === FALSE)\n\t\t{\n\t\t\t$this->CI->output->append_output($template);\n\t\t}\n\n\t\treturn $template;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set the left/right variable delimiters\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tvoid\n\t */\n\tpublic function set_delimiters($l = '{', $r = '}')\n\t{\n\t\t$this->l_delim = $l;\n\t\t$this->r_delim = $r;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Parse a single key/value\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tprotected function _parse_single($key, $val, $string)\n\t{\n\t\treturn array($this->l_delim.$key.$this->r_delim => (string) $val);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Parse a tag pair\n\t *\n\t * Parses tag pairs: {some_tag} string... {/some_tag}\n\t *\n\t * @param\tstring\n\t * @param\tarray\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tprotected function _parse_pair($variable, $data, $string)\n\t{\n\t\t$replace = array();\n\t\tpreg_match_all(\n\t\t\t'#'.preg_quote($this->l_delim.$variable.$this->r_delim).'(.+?)'.preg_quote($this->l_delim.'/'.$variable.$this->r_delim).'#s',\n\t\t\t$string,\n\t\t\t$matches,\n\t\t\tPREG_SET_ORDER\n\t\t);\n\n\t\tforeach ($matches as $match)\n\t\t{\n\t\t\t$str = '';\n\t\t\tforeach ($data as $row)\n\t\t\t{\n\t\t\t\t$temp = array();\n\t\t\t\tforeach ($row as $key => $val)\n\t\t\t\t{\n\t\t\t\t\tif (is_array($val))\n\t\t\t\t\t{\n\t\t\t\t\t\t$pair = $this->_parse_pair($key, $val, $match[1]);\n\t\t\t\t\t\tif ( ! empty($pair))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t$temp = array_merge($temp, $pair);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t$temp[$this->l_delim.$key.$this->r_delim] = $val;\n\t\t\t\t}\n\n\t\t\t\t$str .= strtr($match[1], $temp);\n\t\t\t}\n\n\t\t\t$replace[$match[0]] = $str;\n\t\t}\n\n\t\treturn $replace;\n\t}\n\n}\n"
  },
  {
    "path": "system/libraries/Profiler.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Profiler Class\n *\n * This class enables you to display benchmark, query, and other data\n * in order to help with debugging and optimization.\n *\n * Note: At some point it would be good to move all the HTML in this class\n * into a set of template files in order to allow customization.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tLibraries\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/general/profiling.html\n */\nclass CI_Profiler {\n\n\t/**\n\t * List of profiler sections available to show\n\t *\n\t * @var array\n\t */\n\tprotected $_available_sections = array(\n\t\t'benchmarks',\n\t\t'get',\n\t\t'memory_usage',\n\t\t'post',\n\t\t'uri_string',\n\t\t'controller_info',\n\t\t'queries',\n\t\t'http_headers',\n\t\t'session_data',\n\t\t'config'\n\t);\n\n\t/**\n\t * Number of queries to show before making the additional queries togglable\n\t *\n\t * @var int\n\t */\n\tprotected $_query_toggle_count = 25;\n\n\t/**\n\t * Reference to the CodeIgniter singleton\n\t *\n\t * @var object\n\t */\n\tprotected $CI;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * Initialize Profiler\n\t *\n\t * @param\tarray\t$config\tParameters\n\t */\n\tpublic function __construct($config = array())\n\t{\n\t\t$this->CI =& get_instance();\n\t\t$this->CI->load->language('profiler');\n\n\t\t// default all sections to display\n\t\tforeach ($this->_available_sections as $section)\n\t\t{\n\t\t\tif ( ! isset($config[$section]))\n\t\t\t{\n\t\t\t\t$this->{'_compile_'.$section} = TRUE;\n\t\t\t}\n\t\t}\n\n\t\t$this->set_sections($config);\n\t\tlog_message('info', 'Profiler Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Sections\n\t *\n\t * Sets the private _compile_* properties to enable/disable Profiler sections\n\t *\n\t * @param\tmixed\t$config\n\t * @return\tvoid\n\t */\n\tpublic function set_sections($config)\n\t{\n\t\tif (isset($config['query_toggle_count']))\n\t\t{\n\t\t\t$this->_query_toggle_count = (int) $config['query_toggle_count'];\n\t\t\tunset($config['query_toggle_count']);\n\t\t}\n\n\t\tforeach ($config as $method => $enable)\n\t\t{\n\t\t\tif (in_array($method, $this->_available_sections))\n\t\t\t{\n\t\t\t\t$this->{'_compile_'.$method} = ($enable !== FALSE);\n\t\t\t}\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Auto Profiler\n\t *\n\t * This function cycles through the entire array of mark points and\n\t * matches any two points that are named identically (ending in \"_start\"\n\t * and \"_end\" respectively).  It then compiles the execution times for\n\t * all points and returns it as an array\n\t *\n\t * @return\tarray\n\t */\n\tprotected function _compile_benchmarks()\n\t{\n\t\t$profile = array();\n\t\tforeach ($this->CI->benchmark->marker as $key => $val)\n\t\t{\n\t\t\t// We match the \"end\" marker so that the list ends\n\t\t\t// up in the order that it was defined\n\t\t\tif (preg_match('/(.+?)_end$/i', $key, $match)\n\t\t\t\t&& isset($this->CI->benchmark->marker[$match[1].'_end'], $this->CI->benchmark->marker[$match[1].'_start']))\n\t\t\t{\n\t\t\t\t$profile[$match[1]] = $this->CI->benchmark->elapsed_time($match[1].'_start', $key);\n\t\t\t}\n\t\t}\n\n\t\t// Build a table containing the profile data.\n\t\t// Note: At some point we should turn this into a template that can\n\t\t// be modified. We also might want to make this data available to be logged\n\n\t\t$output = \"\\n\\n\"\n\t\t\t.'<fieldset id=\"ci_profiler_benchmarks\" style=\"border:1px solid #900;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;\">'\n\t\t\t.\"\\n\"\n\t\t\t.'<legend style=\"color:#900;\">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_benchmarks').\"&nbsp;&nbsp;</legend>\"\n\t\t\t.\"\\n\\n\\n<table style=\\\"width:100%;\\\">\\n\";\n\n\t\tforeach ($profile as $key => $val)\n\t\t{\n\t\t\t$key = ucwords(str_replace(array('_', '-'), ' ', $key));\n\t\t\t$output .= '<tr><td style=\"padding:5px;width:50%;color:#000;font-weight:bold;background-color:#ddd;\">'\n\t\t\t\t\t.$key.'&nbsp;&nbsp;</td><td style=\"padding:5px;width:50%;color:#900;font-weight:normal;background-color:#ddd;\">'\n\t\t\t\t\t.$val.\"</td></tr>\\n\";\n\t\t}\n\n\t\treturn $output.\"</table>\\n</fieldset>\";\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Compile Queries\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _compile_queries()\n\t{\n\t\t$dbs = array();\n\n\t\t// Let's determine which databases are currently connected to\n\t\tforeach (get_object_vars($this->CI) as $name => $cobject)\n\t\t{\n\t\t\tif (is_object($cobject))\n\t\t\t{\n\t\t\t\tif ($cobject instanceof CI_DB)\n\t\t\t\t{\n\t\t\t\t\t$dbs[get_class($this->CI).':$'.$name] = $cobject;\n\t\t\t\t}\n\t\t\t\telseif ($cobject instanceof CI_Model)\n\t\t\t\t{\n\t\t\t\t\tforeach (get_object_vars($cobject) as $mname => $mobject)\n\t\t\t\t\t{\n\t\t\t\t\t\tif ($mobject instanceof CI_DB)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t$dbs[get_class($cobject).':$'.$mname] = $mobject;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (count($dbs) === 0)\n\t\t{\n\t\t\treturn \"\\n\\n\"\n\t\t\t\t.'<fieldset id=\"ci_profiler_queries\" style=\"border:1px solid #0000FF;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;\">'\n\t\t\t\t.\"\\n\"\n\t\t\t\t.'<legend style=\"color:#0000FF;\">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_queries').'&nbsp;&nbsp;</legend>'\n\t\t\t\t.\"\\n\\n\\n<table style=\\\"border:none; width:100%;\\\">\\n\"\n\t\t\t\t.'<tr><td style=\"width:100%;color:#0000FF;font-weight:normal;background-color:#eee;padding:5px;\">'\n\t\t\t\t.$this->CI->lang->line('profiler_no_db')\n\t\t\t\t.\"</td></tr>\\n</table>\\n</fieldset>\";\n\t\t}\n\n\t\t// Load the text helper so we can highlight the SQL\n\t\t$this->CI->load->helper('text');\n\n\t\t// Key words we want bolded\n\t\t$highlight = array('SELECT', 'DISTINCT', 'FROM', 'WHERE', 'AND', 'LEFT&nbsp;JOIN', 'ORDER&nbsp;BY', 'GROUP&nbsp;BY', 'LIMIT', 'INSERT', 'INTO', 'VALUES', 'UPDATE', 'OR&nbsp;', 'HAVING', 'OFFSET', 'NOT&nbsp;IN', 'IN', 'LIKE', 'NOT&nbsp;LIKE', 'COUNT', 'MAX', 'MIN', 'ON', 'AS', 'AVG', 'SUM', '(', ')');\n\n\t\t$output  = \"\\n\\n\";\n\t\t$count = 0;\n\n\t\tforeach ($dbs as $name => $db)\n\t\t{\n\t\t\t$hide_queries = (count($db->queries) > $this->_query_toggle_count) ? ' display:none' : '';\n\t\t\t$total_time = number_format(array_sum($db->query_times), 4).' '.$this->CI->lang->line('profiler_seconds');\n\n\t\t\t$show_hide_js = '(<span style=\"cursor: pointer;\" onclick=\"var s=document.getElementById(\\'ci_profiler_queries_db_'.$count.'\\').style;s.display=s.display==\\'none\\'?\\'\\':\\'none\\';this.innerHTML=this.innerHTML==\\''.$this->CI->lang->line('profiler_section_hide').'\\'?\\''.$this->CI->lang->line('profiler_section_show').'\\':\\''.$this->CI->lang->line('profiler_section_hide').'\\';\">'.$this->CI->lang->line('profiler_section_hide').'</span>)';\n\n\t\t\tif ($hide_queries !== '')\n\t\t\t{\n\t\t\t\t$show_hide_js = '(<span style=\"cursor: pointer;\" onclick=\"var s=document.getElementById(\\'ci_profiler_queries_db_'.$count.'\\').style;s.display=s.display==\\'none\\'?\\'\\':\\'none\\';this.innerHTML=this.innerHTML==\\''.$this->CI->lang->line('profiler_section_show').'\\'?\\''.$this->CI->lang->line('profiler_section_hide').'\\':\\''.$this->CI->lang->line('profiler_section_show').'\\';\">'.$this->CI->lang->line('profiler_section_show').'</span>)';\n\t\t\t}\n\n\t\t\t$output .= '<fieldset style=\"border:1px solid #0000FF;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;\">'\n\t\t\t\t.\"\\n\"\n\t\t\t\t.'<legend style=\"color:#0000FF;\">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_database')\n\t\t\t\t.':&nbsp; '.$db->database.' ('.$name.')&nbsp;&nbsp;&nbsp;'.$this->CI->lang->line('profiler_queries')\n\t\t\t\t.': '.count($db->queries).' ('.$total_time.')&nbsp;&nbsp;'.$show_hide_js.\"</legend>\\n\\n\\n\"\n\t\t\t\t.'<table style=\"width:100%;'.$hide_queries.'\" id=\"ci_profiler_queries_db_'.$count.\"\\\">\\n\";\n\n\t\t\tif (count($db->queries) === 0)\n\t\t\t{\n\t\t\t\t$output .= '<tr><td style=\"width:100%;color:#0000FF;font-weight:normal;background-color:#eee;padding:5px;\">'\n\t\t\t\t\t\t.$this->CI->lang->line('profiler_no_queries').\"</td></tr>\\n\";\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tforeach ($db->queries as $key => $val)\n\t\t\t\t{\n\t\t\t\t\t$time = number_format($db->query_times[$key], 4);\n\t\t\t\t\t$val = highlight_code($val);\n\n\t\t\t\t\tforeach ($highlight as $bold)\n\t\t\t\t\t{\n\t\t\t\t\t\t$val = str_replace($bold, '<strong>'.$bold.'</strong>', $val);\n\t\t\t\t\t}\n\n\t\t\t\t\t$output .= '<tr><td style=\"padding:5px;vertical-align:top;width:1%;color:#900;font-weight:normal;background-color:#ddd;\">'\n\t\t\t\t\t\t\t.$time.'&nbsp;&nbsp;</td><td style=\"padding:5px;color:#000;font-weight:normal;background-color:#ddd;\">'\n\t\t\t\t\t\t\t.$val.\"</td></tr>\\n\";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$output .= \"</table>\\n</fieldset>\";\n\t\t\t$count++;\n\t\t}\n\n\t\treturn $output;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Compile $_GET Data\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _compile_get()\n\t{\n\t\t$output = \"\\n\\n\"\n\t\t\t.'<fieldset id=\"ci_profiler_get\" style=\"border:1px solid #cd6e00;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;\">'\n\t\t\t.\"\\n\"\n\t\t\t.'<legend style=\"color:#cd6e00;\">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_get_data').\"&nbsp;&nbsp;</legend>\\n\";\n\n\t\tif (count($_GET) === 0)\n\t\t{\n\t\t\t$output .= '<div style=\"color:#cd6e00;font-weight:normal;padding:4px 0 4px 0;\">'.$this->CI->lang->line('profiler_no_get').'</div>';\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$output .= \"\\n\\n<table style=\\\"width:100%;border:none;\\\">\\n\";\n\n\t\t\tforeach ($_GET as $key => $val)\n\t\t\t{\n\t\t\t\tis_int($key) OR $key = \"'\".htmlspecialchars($key, ENT_QUOTES, config_item('charset')).\"'\";\n\t\t\t\t$val = (is_array($val) OR is_object($val))\n\t\t\t\t\t? '<pre>'.htmlspecialchars(print_r($val, TRUE), ENT_QUOTES, config_item('charset')).'</pre>'\n\t\t\t\t\t: htmlspecialchars($val, ENT_QUOTES, config_item('charset'));\n\n\t\t\t\t$output .= '<tr><td style=\"width:50%;color:#000;background-color:#ddd;padding:5px;\">&#36;_GET['\n\t\t\t\t\t.$key.']&nbsp;&nbsp; </td><td style=\"width:50%;padding:5px;color:#cd6e00;font-weight:normal;background-color:#ddd;\">'\n\t\t\t\t\t.$val.\"</td></tr>\\n\";\n\t\t\t}\n\n\t\t\t$output .= \"</table>\\n\";\n\t\t}\n\n\t\treturn $output.'</fieldset>';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Compile $_POST Data\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _compile_post()\n\t{\n\t\t$output = \"\\n\\n\"\n\t\t\t.'<fieldset id=\"ci_profiler_post\" style=\"border:1px solid #009900;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;\">'\n\t\t\t.\"\\n\"\n\t\t\t.'<legend style=\"color:#009900;\">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_post_data').\"&nbsp;&nbsp;</legend>\\n\";\n\n\t\tif (count($_POST) === 0 && count($_FILES) === 0)\n\t\t{\n\t\t\t$output .= '<div style=\"color:#009900;font-weight:normal;padding:4px 0 4px 0;\">'.$this->CI->lang->line('profiler_no_post').'</div>';\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$output .= \"\\n\\n<table style=\\\"width:100%;\\\">\\n\";\n\n\t\t\tforeach ($_POST as $key => $val)\n\t\t\t{\n\t\t\t\tis_int($key) OR $key = \"'\".htmlspecialchars($key, ENT_QUOTES, config_item('charset')).\"'\";\n\t\t\t\t$val = (is_array($val) OR is_object($val))\n\t\t\t\t\t? '<pre>'.htmlspecialchars(print_r($val, TRUE), ENT_QUOTES, config_item('charset')).'</pre>'\n\t\t\t\t\t: htmlspecialchars($val, ENT_QUOTES, config_item('charset'));\n\n\t\t\t\t$output .= '<tr><td style=\"width:50%;padding:5px;color:#000;background-color:#ddd;\">&#36;_POST['\n\t\t\t\t\t.$key.']&nbsp;&nbsp; </td><td style=\"width:50%;padding:5px;color:#009900;font-weight:normal;background-color:#ddd;\">'\n\t\t\t\t\t.$val.\"</td></tr>\\n\";\n\t\t\t}\n\n\t\t\tforeach ($_FILES as $key => $val)\n\t\t\t{\n\t\t\t\tis_int($key) OR $key = \"'\".htmlspecialchars($key, ENT_QUOTES, config_item('charset')).\"'\";\n\t\t\t\t$val = (is_array($val) OR is_object($val))\n\t\t\t\t\t? '<pre>'.htmlspecialchars(print_r($val, TRUE), ENT_QUOTES, config_item('charset')).'</pre>'\n\t\t\t\t\t: htmlspecialchars($val, ENT_QUOTES, config_item('charset'));\n\n\t\t\t\t$output .= '<tr><td style=\"width:50%;padding:5px;color:#000;background-color:#ddd;\">&#36;_FILES['\n\t\t\t\t\t.$key.']&nbsp;&nbsp; </td><td style=\"width:50%;padding:5px;color:#009900;font-weight:normal;background-color:#ddd;\">'\n\t\t\t\t\t.$val.\"</td></tr>\\n\";\n\t\t\t}\n\n\t\t\t$output .= \"</table>\\n\";\n\t\t}\n\n\t\treturn $output.'</fieldset>';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show query string\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _compile_uri_string()\n\t{\n\t\treturn \"\\n\\n\"\n\t\t\t.'<fieldset id=\"ci_profiler_uri_string\" style=\"border:1px solid #000;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;\">'\n\t\t\t.\"\\n\"\n\t\t\t.'<legend style=\"color:#000;\">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_uri_string').\"&nbsp;&nbsp;</legend>\\n\"\n\t\t\t.'<div style=\"color:#000;font-weight:normal;padding:4px 0 4px 0;\">'\n\t\t\t.($this->CI->uri->uri_string === '' ? $this->CI->lang->line('profiler_no_uri') : $this->CI->uri->uri_string)\n\t\t\t.'</div></fieldset>';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show the controller and function that were called\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _compile_controller_info()\n\t{\n\t\treturn \"\\n\\n\"\n\t\t\t.'<fieldset id=\"ci_profiler_controller_info\" style=\"border:1px solid #995300;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;\">'\n\t\t\t.\"\\n\"\n\t\t\t.'<legend style=\"color:#995300;\">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_controller_info').\"&nbsp;&nbsp;</legend>\\n\"\n\t\t\t.'<div style=\"color:#995300;font-weight:normal;padding:4px 0 4px 0;\">'.$this->CI->router->class.'/'.$this->CI->router->method\n\t\t\t.'</div></fieldset>';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Compile memory usage\n\t *\n\t * Display total used memory\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _compile_memory_usage()\n\t{\n\t\treturn \"\\n\\n\"\n\t\t\t.'<fieldset id=\"ci_profiler_memory_usage\" style=\"border:1px solid #5a0099;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;\">'\n\t\t\t.\"\\n\"\n\t\t\t.'<legend style=\"color:#5a0099;\">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_memory_usage').\"&nbsp;&nbsp;</legend>\\n\"\n\t\t\t.'<div style=\"color:#5a0099;font-weight:normal;padding:4px 0 4px 0;\">'\n\t\t\t.(($usage = memory_get_usage()) != '' ? number_format($usage).' bytes' : $this->CI->lang->line('profiler_no_memory'))\n\t\t\t.'</div></fieldset>';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Compile header information\n\t *\n\t * Lists HTTP headers\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _compile_http_headers()\n\t{\n\t\t$output = \"\\n\\n\"\n\t\t\t.'<fieldset id=\"ci_profiler_http_headers\" style=\"border:1px solid #000;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;\">'\n\t\t\t.\"\\n\"\n\t\t\t.'<legend style=\"color:#000;\">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_headers')\n\t\t\t.'&nbsp;&nbsp;(<span style=\"cursor: pointer;\" onclick=\"var s=document.getElementById(\\'ci_profiler_httpheaders_table\\').style;s.display=s.display==\\'none\\'?\\'\\':\\'none\\';this.innerHTML=this.innerHTML==\\''.$this->CI->lang->line('profiler_section_show').'\\'?\\''.$this->CI->lang->line('profiler_section_hide').'\\':\\''.$this->CI->lang->line('profiler_section_show').'\\';\">'.$this->CI->lang->line('profiler_section_show').\"</span>)</legend>\\n\\n\\n\"\n\t\t\t.'<table style=\"width:100%;display:none;\" id=\"ci_profiler_httpheaders_table\">'.\"\\n\";\n\n\t\tforeach (array('HTTP_ACCEPT', 'HTTP_USER_AGENT', 'HTTP_CONNECTION', 'SERVER_PORT', 'SERVER_NAME', 'REMOTE_ADDR', 'SERVER_SOFTWARE', 'HTTP_ACCEPT_LANGUAGE', 'SCRIPT_NAME', 'REQUEST_METHOD',' HTTP_HOST', 'REMOTE_HOST', 'CONTENT_TYPE', 'SERVER_PROTOCOL', 'QUERY_STRING', 'HTTP_ACCEPT_ENCODING', 'HTTP_X_FORWARDED_FOR', 'HTTP_DNT') as $header)\n\t\t{\n\t\t\t$val = isset($_SERVER[$header]) ? htmlspecialchars($_SERVER[$header], ENT_QUOTES, config_item('charset')) : '';\n\t\t\t$output .= '<tr><td style=\"vertical-align:top;width:50%;padding:5px;color:#900;background-color:#ddd;\">'\n\t\t\t\t.$header.'&nbsp;&nbsp;</td><td style=\"width:50%;padding:5px;color:#000;background-color:#ddd;\">'.$val.\"</td></tr>\\n\";\n\t\t}\n\n\t\treturn $output.\"</table>\\n</fieldset>\";\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Compile config information\n\t *\n\t * Lists developer config variables\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _compile_config()\n\t{\n\t\t$output = \"\\n\\n\"\n\t\t\t.'<fieldset id=\"ci_profiler_config\" style=\"border:1px solid #000;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;\">'\n\t\t\t.\"\\n\"\n\t\t\t.'<legend style=\"color:#000;\">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_config').'&nbsp;&nbsp;(<span style=\"cursor: pointer;\" onclick=\"var s=document.getElementById(\\'ci_profiler_config_table\\').style;s.display=s.display==\\'none\\'?\\'\\':\\'none\\';this.innerHTML=this.innerHTML==\\''.$this->CI->lang->line('profiler_section_show').'\\'?\\''.$this->CI->lang->line('profiler_section_hide').'\\':\\''.$this->CI->lang->line('profiler_section_show').'\\';\">'.$this->CI->lang->line('profiler_section_show').\"</span>)</legend>\\n\\n\\n\"\n\t\t\t.'<table style=\"width:100%;display:none;\" id=\"ci_profiler_config_table\">'.\"\\n\";\n\n\t\tforeach ($this->CI->config->config as $config => $val)\n\t\t{\n\t\t\t$pre       = '';\n\t\t\t$pre_close = '';\n\n\t\t\tif (is_array($val) OR is_object($val))\n\t\t\t{\n\t\t\t\t$val = print_r($val, TRUE);\n\n\t\t\t\t$pre       = '<pre>' ;\n\t\t\t\t$pre_close = '</pre>';\n\t\t\t}\n\n\t\t\t$output .= '<tr><td style=\"padding:5px;vertical-align:top;color:#900;background-color:#ddd;\">'\n\t\t\t\t.$config.'&nbsp;&nbsp;</td><td style=\"padding:5px;color:#000;background-color:#ddd;\">'.$pre.htmlspecialchars((string) $val, ENT_QUOTES, config_item('charset')).$pre_close.\"</td></tr>\\n\";\n\t\t}\n\n\t\treturn $output.\"</table>\\n</fieldset>\";\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Compile session userdata\n\t *\n\t * @return \tstring\n\t */\n\tprotected function _compile_session_data()\n\t{\n\t\tif ( ! isset($this->CI->session))\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t$output = '<fieldset id=\"ci_profiler_csession\" style=\"border:1px solid #000;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;\">'\n\t\t\t.'<legend style=\"color:#000;\">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_session_data').'&nbsp;&nbsp;(<span style=\"cursor: pointer;\" onclick=\"var s=document.getElementById(\\'ci_profiler_session_data\\').style;s.display=s.display==\\'none\\'?\\'\\':\\'none\\';this.innerHTML=this.innerHTML==\\''.$this->CI->lang->line('profiler_section_show').'\\'?\\''.$this->CI->lang->line('profiler_section_hide').'\\':\\''.$this->CI->lang->line('profiler_section_show').'\\';\">'.$this->CI->lang->line('profiler_section_show').'</span>)</legend>'\n\t\t\t.'<table style=\"width:100%;display:none;\" id=\"ci_profiler_session_data\">';\n\n\t\tforeach ($this->CI->session->userdata() as $key => $val)\n\t\t{\n\t\t\t$pre       = '';\n\t\t\t$pre_close = '';\n\n\t\t\tif (is_array($val) OR is_object($val))\n\t\t\t{\n\t\t\t\t$val = print_r($val, TRUE);\n\n\t\t\t\t$pre       = '<pre>' ;\n\t\t\t\t$pre_close = '</pre>';\n\t\t\t}\n\n\t\t\t$output .= '<tr><td style=\"padding:5px;vertical-align:top;color:#900;background-color:#ddd;\">'\n\t\t\t\t.$key.'&nbsp;&nbsp;</td><td style=\"padding:5px;color:#000;background-color:#ddd;\">'.$pre.htmlspecialchars((string) $val, ENT_QUOTES, config_item('charset')).$pre_close.\"</td></tr>\\n\";\n\t\t}\n\n\t\treturn $output.\"</table>\\n</fieldset>\";\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Run the Profiler\n\t *\n\t * @return\tstring\n\t */\n\tpublic function run()\n\t{\n\t\t$output = '<div id=\"codeigniter_profiler\" style=\"clear:both;background-color:#fff;padding:10px;\">';\n\t\t$fields_displayed = 0;\n\n\t\tforeach ($this->_available_sections as $section)\n\t\t{\n\t\t\tif ($this->{'_compile_'.$section} !== FALSE)\n\t\t\t{\n\t\t\t\t$func = '_compile_'.$section;\n\t\t\t\t$output .= $this->{$func}();\n\t\t\t\t$fields_displayed++;\n\t\t\t}\n\t\t}\n\n\t\tif ($fields_displayed === 0)\n\t\t{\n\t\t\t$output .= '<p style=\"border:1px solid #5a0099;padding:10px;margin:20px 0;background-color:#eee;\">'\n\t\t\t\t.$this->CI->lang->line('profiler_no_profiles').'</p>';\n\t\t}\n\n\t\treturn $output.'</div>';\n\t}\n\n}\n"
  },
  {
    "path": "system/libraries/Session/CI_Session_driver_interface.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CI_Session_driver_interface\n *\n * A compatibility typeless SessionHandlerInterface alias\n *\n * @package\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tSessions\n * @author\tAndrey Andreev\n * @link\thttps://codeigniter.com/userguide3/libraries/sessions.html\n */\ninterface CI_Session_driver_interface {\n\n\tpublic function open($save_path, $name);\n\tpublic function close();\n\tpublic function read($session_id);\n\tpublic function write($session_id, $session_data);\n\tpublic function destroy($session_id);\n\tpublic function gc($maxlifetime);\n\tpublic function updateTimestamp($session_id, $data);\n\tpublic function validateId($session_id);\n}\n"
  },
  {
    "path": "system/libraries/Session/OldSessionWrapper.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * OldSessionWrapper\n *\n * PHP 8 Session handler compatibility wrapper, pre-PHP8 version\n *\n * @package\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tSessions\n * @author\tAndrey Andreev\n * @link\thttps://codeigniter.com/userguide3/libraries/sessions.html\n */\nclass CI_SessionWrapper implements SessionHandlerInterface, SessionUpdateTimestampHandlerInterface {\n\n\tprotected $driver;\n\n\tpublic function __construct(CI_Session_driver_interface $driver)\n\t{\n\t\t$this->driver = $driver;\n\t}\n\n\tpublic function open($save_path, $name)\n\t{\n\t\treturn $this->driver->open($save_path, $name);\n\t}\n\n\tpublic function close()\n\t{\n\t\treturn $this->driver->close();\n\t}\n\n\tpublic function read($id)\n\t{\n\t\treturn $this->driver->read($id);\n\t}\n\n\tpublic function write($id, $data)\n\t{\n\t\treturn $this->driver->write($id, $data);\n\t}\n\n\tpublic function destroy($id)\n\t{\n\t\treturn $this->driver->destroy($id);\n\t}\n\n\tpublic function gc($maxlifetime)\n\t{\n\t\treturn $this->driver->gc($maxlifetime);\n\t}\n\n\tpublic function updateTimestamp($id, $data)\n\t{\n\t\treturn $this->driver->updateTimestamp($id, $data);\n\t}\n\n\tpublic function validateId($id)\n\t{\n\t\treturn $this->driver->validateId($id);\n\t}\n}\n"
  },
  {
    "path": "system/libraries/Session/PHP8SessionWrapper.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * PHP8SessionWrapper\n *\n * PHP 8 Session handler compatibility wrapper\n *\n * @package\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tSessions\n * @author\tAndrey Andreev\n * @link\thttps://codeigniter.com/userguide3/libraries/sessions.html\n */\nclass CI_SessionWrapper implements SessionHandlerInterface, SessionUpdateTimestampHandlerInterface {\n\n\tprotected CI_Session_driver_interface $driver;\n\n\tpublic function __construct(CI_Session_driver_interface $driver)\n\t{\n\t\t$this->driver = $driver;\n\t}\n\n\tpublic function open(string $save_path, string $name): bool\n\t{\n\t\treturn $this->driver->open($save_path, $name);\n\t}\n\n\tpublic function close(): bool\n\t{\n\t\treturn $this->driver->close();\n\t}\n\n\t#[\\ReturnTypeWillChange]\n\tpublic function read(string $id): mixed\n\t{\n\t\treturn $this->driver->read($id);\n\t}\n\n\tpublic function write(string $id, string $data): bool\n\t{\n\t\treturn $this->driver->write($id, $data);\n\t}\n\n\tpublic function destroy(string $id): bool\n\t{\n\t\treturn $this->driver->destroy($id);\n\t}\n\n\t#[\\ReturnTypeWillChange]\n\tpublic function gc(int $maxlifetime): mixed\n\t{\n\t\treturn $this->driver->gc($maxlifetime);\n\t}\n\n\tpublic function updateTimestamp(string $id, string$data): bool\n\t{\n\t\treturn $this->driver->updateTimestamp($id, $data);\n\t}\n\n\tpublic function validateId(string $id): bool\n\t{\n\t\treturn $this->driver->validateId($id);\n\t}\n}\n"
  },
  {
    "path": "system/libraries/Session/Session.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 2.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Session Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tSessions\n * @author\t\tAndrey Andreev\n * @link\t\thttps://codeigniter.com/userguide3/libraries/sessions.html\n */\nclass CI_Session {\n\n\t/**\n\t * Userdata array\n\t *\n\t * Just a reference to $_SESSION, for BC purposes.\n\t */\n\tpublic $userdata;\n\n\tprotected $_driver = 'files';\n\tprotected $_config;\n\tprotected $_sid_regexp;\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * @param\tarray\t$params\tConfiguration parameters\n\t * @return\tvoid\n\t */\n\tpublic function __construct(array $params = array())\n\t{\n\t\t// No sessions under CLI\n\t\tif (is_cli())\n\t\t{\n\t\t\tlog_message('debug', 'Session: Initialization under CLI aborted.');\n\t\t\treturn;\n\t\t}\n\t\telseif ((bool) ini_get('session.auto_start'))\n\t\t{\n\t\t\tlog_message('error', 'Session: session.auto_start is enabled in php.ini. Aborting.');\n\t\t\treturn;\n\t\t}\n\t\telseif ( ! empty($params['driver']))\n\t\t{\n\t\t\t$this->_driver = $params['driver'];\n\t\t\tunset($params['driver']);\n\t\t}\n\t\telseif ($driver = config_item('sess_driver'))\n\t\t{\n\t\t\t$this->_driver = $driver;\n\t\t}\n\t\t// Note: BC workaround\n\t\telseif (config_item('sess_use_database'))\n\t\t{\n\t\t\tlog_message('debug', 'Session: \"sess_driver\" is empty; using BC fallback to \"sess_use_database\".');\n\t\t\t$this->_driver = 'database';\n\t\t}\n\n\t\t$class = $this->_ci_load_classes($this->_driver);\n\n\t\t// Configuration ...\n\t\t$this->_configure($params);\n\t\t$this->_config['_sid_regexp'] = $this->_sid_regexp;\n\n\t\t$class   = new $class($this->_config);\n\t\t$wrapper = new CI_SessionWrapper($class);\n\t\tif (is_php('5.4'))\n\t\t{\n\t\t\tsession_set_save_handler($wrapper, TRUE);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsession_set_save_handler(\n\t\t\t\tarray($wrapper, 'open'),\n\t\t\t\tarray($wrapper, 'close'),\n\t\t\t\tarray($wrapper, 'read'),\n\t\t\t\tarray($wrapper, 'write'),\n\t\t\t\tarray($wrapper, 'destroy'),\n\t\t\t\tarray($wrapper, 'gc')\n\t\t\t);\n\n\t\t\tregister_shutdown_function('session_write_close');\n\t\t}\n\n\t\t// Sanitize the cookie, because apparently PHP doesn't do that for userspace handlers\n\t\tif (isset($_COOKIE[$this->_config['cookie_name']])\n\t\t\t&& (\n\t\t\t\t! is_string($_COOKIE[$this->_config['cookie_name']])\n\t\t\t\tOR ! preg_match('#\\A'.$this->_sid_regexp.'\\z#', $_COOKIE[$this->_config['cookie_name']])\n\t\t\t)\n\t\t)\n\t\t{\n\t\t\tunset($_COOKIE[$this->_config['cookie_name']]);\n\t\t}\n\n\t\tsession_start();\n\n\t\t// Is session ID auto-regeneration configured? (ignoring ajax requests)\n\t\tif ((empty($_SERVER['HTTP_X_REQUESTED_WITH']) OR strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest')\n\t\t\t&& ($regenerate_time = config_item('sess_time_to_update')) > 0\n\t\t)\n\t\t{\n\t\t\tif ( ! isset($_SESSION['__ci_last_regenerate']))\n\t\t\t{\n\t\t\t\t$_SESSION['__ci_last_regenerate'] = time();\n\t\t\t}\n\t\t\telseif ($_SESSION['__ci_last_regenerate'] < (time() - $regenerate_time))\n\t\t\t{\n\t\t\t\t$this->sess_regenerate((bool) config_item('sess_regenerate_destroy'));\n\t\t\t}\n\t\t}\n\t\t// Another work-around ... PHP doesn't seem to send the session cookie\n\t\t// unless it is being currently created or regenerated\n\t\telseif (isset($_COOKIE[$this->_config['cookie_name']]) && $_COOKIE[$this->_config['cookie_name']] === session_id())\n\t\t{\n\t\t\t$expires = empty($this->_config['cookie_lifetime']) ? 0 : time() + $this->_config['cookie_lifetime'];\n\t\t\tif (is_php('7.3'))\n\t\t\t{\n\t\t\t\tsetcookie(\n\t\t\t\t\t$this->_config['cookie_name'],\n\t\t\t\t\tsession_id(),\n\t\t\t\t\tarray(\n\t\t\t\t\t\t'expires' => $expires,\n\t\t\t\t\t\t'path' => $this->_config['cookie_path'],\n\t\t\t\t\t\t'domain' => $this->_config['cookie_domain'],\n\t\t\t\t\t\t'secure' => $this->_config['cookie_secure'],\n\t\t\t\t\t\t'httponly' => TRUE,\n\t\t\t\t\t\t'samesite' => $this->_config['cookie_samesite']\n\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$header = 'Set-Cookie: '.$this->_config['cookie_name'].'='.session_id();\n\t\t\t\t$header .= empty($expires) ? '' : '; Expires='.gmdate('D, d-M-Y H:i:s T', $expires).'; Max-Age='.$this->_config['cookie_lifetime'];\n\t\t\t\t$header .= '; Path='.$this->_config['cookie_path'];\n\t\t\t\t$header .= ($this->_config['cookie_domain'] !== '' ? '; Domain='.$this->_config['cookie_domain'] : '');\n\t\t\t\t$header .= ($this->_config['cookie_secure'] ? '; Secure' : '').'; HttpOnly; SameSite='.$this->_config['cookie_samesite'];\n\t\t\t\theader($header);\n\t\t\t}\n\n\t\t\tif ( ! $this->_config['cookie_secure'] && $this->_config['cookie_samesite'] === 'None')\n\t\t\t{\n\t\t\t\tlog_message('error', \"Session: '\".$this->_config['cookie_name'].\"' cookie sent with SameSite=None, but without Secure attribute.'\");\n\t\t\t}\n\t\t}\n\n\t\t$this->_ci_init_vars();\n\n\t\tlog_message('info', \"Session: Class initialized using '\".$this->_driver.\"' driver.\");\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * CI Load Classes\n\t *\n\t * An internal method to load all possible dependency and extension\n\t * classes. It kind of emulates the CI_Driver library, but is\n\t * self-sufficient.\n\t *\n\t * @param\tstring\t$driver\tDriver name\n\t * @return\tstring\tDriver class name\n\t */\n\tprotected function _ci_load_classes($driver)\n\t{\n\t\t// PHP 7 compatibility\n\t\tinterface_exists('SessionUpdateTimestampHandlerInterface', FALSE) OR require_once(BASEPATH.'libraries/Session/SessionUpdateTimestampHandlerInterface.php');\n\n\t\trequire_once(BASEPATH.'libraries/Session/CI_Session_driver_interface.php');\n\t\t$wrapper = is_php('8.0') ? 'PHP8SessionWrapper' : 'OldSessionWrapper';\n\t\trequire_once(BASEPATH.'libraries/Session/'.$wrapper.'.php');\n\n\t\t$prefix = config_item('subclass_prefix');\n\n\t\tif ( ! class_exists('CI_Session_driver', FALSE))\n\t\t{\n\t\t\trequire_once(\n\t\t\t\tfile_exists(APPPATH.'libraries/Session/Session_driver.php')\n\t\t\t\t\t? APPPATH.'libraries/Session/Session_driver.php'\n\t\t\t\t\t: BASEPATH.'libraries/Session/Session_driver.php'\n\t\t\t);\n\n\t\t\tif (file_exists($file_path = APPPATH.'libraries/Session/'.$prefix.'Session_driver.php'))\n\t\t\t{\n\t\t\t\trequire_once($file_path);\n\t\t\t}\n\t\t}\n\n\t\t$class = 'Session_'.$driver.'_driver';\n\n\t\t// Allow custom drivers without the CI_ or MY_ prefix\n\t\tif ( ! class_exists($class, FALSE) && file_exists($file_path = APPPATH.'libraries/Session/drivers/'.$class.'.php'))\n\t\t{\n\t\t\trequire_once($file_path);\n\t\t\tif (class_exists($class, FALSE))\n\t\t\t{\n\t\t\t\treturn $class;\n\t\t\t}\n\t\t}\n\n\t\tif ( ! class_exists('CI_'.$class, FALSE))\n\t\t{\n\t\t\tif (file_exists($file_path = APPPATH.'libraries/Session/drivers/'.$class.'.php') OR file_exists($file_path = BASEPATH.'libraries/Session/drivers/'.$class.'.php'))\n\t\t\t{\n\t\t\t\trequire_once($file_path);\n\t\t\t}\n\n\t\t\tif ( ! class_exists('CI_'.$class, FALSE) && ! class_exists($class, FALSE))\n\t\t\t{\n\t\t\t\tthrow new UnexpectedValueException(\"Session: Configured driver '\".$driver.\"' was not found. Aborting.\");\n\t\t\t}\n\t\t}\n\n\t\tif ( ! class_exists($prefix.$class, FALSE) && file_exists($file_path = APPPATH.'libraries/Session/drivers/'.$prefix.$class.'.php'))\n\t\t{\n\t\t\trequire_once($file_path);\n\t\t\tif (class_exists($prefix.$class, FALSE))\n\t\t\t{\n\t\t\t\treturn $prefix.$class;\n\t\t\t}\n\n\t\t\tlog_message('debug', 'Session: '.$prefix.$class.\".php found but it doesn't declare class \".$prefix.$class.'.');\n\t\t}\n\n\t\treturn 'CI_'.$class;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Configuration\n\t *\n\t * Handle input parameters and configuration defaults\n\t *\n\t * @param\tarray\t&$params\tInput parameters\n\t * @return\tvoid\n\t */\n\tprotected function _configure(&$params)\n\t{\n\t\t$expiration = config_item('sess_expiration');\n\n\t\tif (isset($params['cookie_lifetime']))\n\t\t{\n\t\t\t$params['cookie_lifetime'] = (int) $params['cookie_lifetime'];\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$params['cookie_lifetime'] = ( ! isset($expiration) && config_item('sess_expire_on_close'))\n\t\t\t\t? 0 : (int) $expiration;\n\t\t}\n\n\t\tisset($params['cookie_name']) OR $params['cookie_name'] = config_item('sess_cookie_name');\n\t\tif (empty($params['cookie_name']))\n\t\t{\n\t\t\t$params['cookie_name'] = ini_get('session.name');\n\t\t}\n\t\telse\n\t\t{\n\t\t\tini_set('session.name', $params['cookie_name']);\n\t\t}\n\n\t\tisset($params['cookie_path']) OR $params['cookie_path'] = config_item('cookie_path');\n\t\tisset($params['cookie_domain']) OR $params['cookie_domain'] = config_item('cookie_domain');\n\t\tisset($params['cookie_secure']) OR $params['cookie_secure'] = (bool) config_item('cookie_secure');\n\n\t\tisset($params['cookie_samesite']) OR $params['cookie_samesite'] = config_item('sess_samesite');\n\t\tif ( ! isset($params['cookie_samesite']) && is_php('7.3'))\n\t\t{\n\t\t\t$params['cookie_samesite'] = ini_get('session.cookie_samesite');\n\t\t}\n\n\t\tif (isset($params['cookie_samesite']))\n\t\t{\n\t\t\t$params['cookie_samesite'] = ucfirst(strtolower($params['cookie_samesite']));\n\t\t\tin_array($params['cookie_samesite'], array('Lax', 'Strict', 'None'), TRUE) OR $params['cookie_samesite'] = 'Lax';\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$params['cookie_samesite'] = 'Lax';\n\t\t}\n\n\t\tif (is_php('7.3'))\n\t\t{\n\t\t\tsession_set_cookie_params(array(\n\t\t\t\t'lifetime' => $params['cookie_lifetime'],\n\t\t\t\t'path'     => $params['cookie_path'],\n\t\t\t\t'domain'   => $params['cookie_domain'],\n\t\t\t\t'secure'   => $params['cookie_secure'],\n\t\t\t\t'httponly' => TRUE,\n\t\t\t\t'samesite' => $params['cookie_samesite']\n\t\t\t));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsession_set_cookie_params(\n\t\t\t\t$params['cookie_lifetime'],\n\t\t\t\t$params['cookie_path'].'; SameSite='.$params['cookie_samesite'],\n\t\t\t\t$params['cookie_domain'],\n\t\t\t\t$params['cookie_secure'],\n\t\t\t\tTRUE // HttpOnly; Yes, this is intentional and not configurable for security reasons\n\t\t\t);\n\t\t}\n\n\t\tif (empty($expiration))\n\t\t{\n\t\t\t$params['expiration'] = (int) ini_get('session.gc_maxlifetime');\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$params['expiration'] = (int) $expiration;\n\t\t\tini_set('session.gc_maxlifetime', $expiration);\n\t\t}\n\n\t\t$params['match_ip'] = (bool) (isset($params['match_ip']) ? $params['match_ip'] : config_item('sess_match_ip'));\n\n\t\tisset($params['save_path']) OR $params['save_path'] = config_item('sess_save_path');\n\n\t\t$this->_config = $params;\n\n\t\t// Security is king\n\t\tini_set('session.use_trans_sid', 0);\n\t\tini_set('session.use_strict_mode', 1);\n\t\tini_set('session.use_cookies', 1);\n\t\tini_set('session.use_only_cookies', 1);\n\n\t\t$this->_configure_sid_length();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Configure session ID length\n\t *\n\t * To make life easier, we used to force SHA-1 and 4 bits per\n\t * character on everyone. And of course, someone was unhappy.\n\t *\n\t * Then PHP 7.1 broke backwards-compatibility because ext/session\n\t * is such a mess that nobody wants to touch it with a pole stick,\n\t * and the one guy who does, nobody has the energy to argue with.\n\t *\n\t * So we were forced to make changes, and OF COURSE something was\n\t * going to break and now we have this pile of shit. -- Narf\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _configure_sid_length()\n\t{\n\t\tif (PHP_VERSION_ID < 70100)\n\t\t{\n\t\t\t$hash_function = ini_get('session.hash_function');\n\t\t\tif (ctype_digit($hash_function))\n\t\t\t{\n\t\t\t\tif ($hash_function !== '1')\n\t\t\t\t{\n\t\t\t\t\tini_set('session.hash_function', 1);\n\t\t\t\t}\n\n\t\t\t\t$bits = 160;\n\t\t\t}\n\t\t\telseif ( ! in_array($hash_function, hash_algos(), TRUE))\n\t\t\t{\n\t\t\t\tini_set('session.hash_function', 1);\n\t\t\t\t$bits = 160;\n\t\t\t}\n\t\t\telseif (($bits = strlen(hash($hash_function, 'dummy', false)) * 4) < 160)\n\t\t\t{\n\t\t\t\tini_set('session.hash_function', 1);\n\t\t\t\t$bits = 160;\n\t\t\t}\n\n\t\t\t$bits_per_character = (int) ini_get('session.hash_bits_per_character');\n\t\t\t$sid_length         = (int) ceil($bits / $bits_per_character);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$bits_per_character = (int) ini_get('session.sid_bits_per_character');\n\t\t\t$sid_length         = (int) ini_get('session.sid_length');\n\t\t\tif (($bits = $sid_length * $bits_per_character) < 160)\n\t\t\t{\n\t\t\t\t// Add as many more characters as necessary to reach at least 160 bits\n\t\t\t\t$sid_length += (int) ceil((160 % $bits) / $bits_per_character);\n\t\t\t\tini_set('session.sid_length', $sid_length);\n\t\t\t}\n\t\t}\n\n\t\t// Yes, 4,5,6 are the only known possible values as of 2016-10-27\n\t\tswitch ($bits_per_character)\n\t\t{\n\t\t\tcase 4:\n\t\t\t\t$this->_sid_regexp = '[0-9a-f]';\n\t\t\t\tbreak;\n\t\t\tcase 5:\n\t\t\t\t$this->_sid_regexp = '[0-9a-v]';\n\t\t\t\tbreak;\n\t\t\tcase 6:\n\t\t\t\t$this->_sid_regexp = '[0-9a-zA-Z,-]';\n\t\t\t\tbreak;\n\t\t}\n\n\t\t$this->_sid_regexp .= '{'.$sid_length.'}';\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Handle temporary variables\n\t *\n\t * Clears old \"flash\" data, marks the new one for deletion and handles\n\t * \"temp\" data deletion.\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _ci_init_vars()\n\t{\n\t\tif ( ! empty($_SESSION['__ci_vars']))\n\t\t{\n\t\t\t$current_time = time();\n\n\t\t\tforeach ($_SESSION['__ci_vars'] as $key => &$value)\n\t\t\t{\n\t\t\t\tif ($value === 'new')\n\t\t\t\t{\n\t\t\t\t\t$_SESSION['__ci_vars'][$key] = 'old';\n\t\t\t\t}\n\t\t\t\telseif ($value === 'old' || $value < $current_time)\n\t\t\t\t{\n\t\t\t\t\tunset($_SESSION[$key], $_SESSION['__ci_vars'][$key]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (empty($_SESSION['__ci_vars']))\n\t\t\t{\n\t\t\t\tunset($_SESSION['__ci_vars']);\n\t\t\t}\n\t\t}\n\n\t\t$this->userdata =& $_SESSION;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Mark as flash\n\t *\n\t * @param\tmixed\t$key\tSession data key(s)\n\t * @return\tbool\n\t */\n\tpublic function mark_as_flash($key)\n\t{\n\t\tif (is_array($key))\n\t\t{\n\t\t\tfor ($i = 0, $c = count($key); $i < $c; $i++)\n\t\t\t{\n\t\t\t\tif ( ! isset($_SESSION[$key[$i]]))\n\t\t\t\t{\n\t\t\t\t\treturn FALSE;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$new = array_fill_keys($key, 'new');\n\n\t\t\t$_SESSION['__ci_vars'] = isset($_SESSION['__ci_vars'])\n\t\t\t\t? array_merge($_SESSION['__ci_vars'], $new)\n\t\t\t\t: $new;\n\n\t\t\treturn TRUE;\n\t\t}\n\n\t\tif ( ! isset($_SESSION[$key]))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$_SESSION['__ci_vars'][$key] = 'new';\n\t\treturn TRUE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get flash keys\n\t *\n\t * @return\tarray\n\t */\n\tpublic function get_flash_keys()\n\t{\n\t\tif ( ! isset($_SESSION['__ci_vars']))\n\t\t{\n\t\t\treturn array();\n\t\t}\n\n\t\t$keys = array();\n\t\tforeach (array_keys($_SESSION['__ci_vars']) as $key)\n\t\t{\n\t\t\tis_int($_SESSION['__ci_vars'][$key]) OR $keys[] = $key;\n\t\t}\n\n\t\treturn $keys;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Unmark flash\n\t *\n\t * @param\tmixed\t$key\tSession data key(s)\n\t * @return\tvoid\n\t */\n\tpublic function unmark_flash($key)\n\t{\n\t\tif (empty($_SESSION['__ci_vars']))\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tis_array($key) OR $key = array($key);\n\n\t\tforeach ($key as $k)\n\t\t{\n\t\t\tif (isset($_SESSION['__ci_vars'][$k]) && ! is_int($_SESSION['__ci_vars'][$k]))\n\t\t\t{\n\t\t\t\tunset($_SESSION['__ci_vars'][$k]);\n\t\t\t}\n\t\t}\n\n\t\tif (empty($_SESSION['__ci_vars']))\n\t\t{\n\t\t\tunset($_SESSION['__ci_vars']);\n\t\t}\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Mark as temp\n\t *\n\t * @param\tmixed\t$key\tSession data key(s)\n\t * @param\tint\t$ttl\tTime-to-live in seconds\n\t * @return\tbool\n\t */\n\tpublic function mark_as_temp($key, $ttl = 300)\n\t{\n\t\t$ttl += time();\n\n\t\tif (is_array($key))\n\t\t{\n\t\t\t$temp = array();\n\n\t\t\tforeach ($key as $k => $v)\n\t\t\t{\n\t\t\t\t// Do we have a key => ttl pair, or just a key?\n\t\t\t\tif (is_int($k))\n\t\t\t\t{\n\t\t\t\t\t$k = $v;\n\t\t\t\t\t$v = $ttl;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$v += time();\n\t\t\t\t}\n\n\t\t\t\tif ( ! isset($_SESSION[$k]))\n\t\t\t\t{\n\t\t\t\t\treturn FALSE;\n\t\t\t\t}\n\n\t\t\t\t$temp[$k] = $v;\n\t\t\t}\n\n\t\t\t$_SESSION['__ci_vars'] = isset($_SESSION['__ci_vars'])\n\t\t\t\t? array_merge($_SESSION['__ci_vars'], $temp)\n\t\t\t\t: $temp;\n\n\t\t\treturn TRUE;\n\t\t}\n\n\t\tif ( ! isset($_SESSION[$key]))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$_SESSION['__ci_vars'][$key] = $ttl;\n\t\treturn TRUE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get temp keys\n\t *\n\t * @return\tarray\n\t */\n\tpublic function get_temp_keys()\n\t{\n\t\tif ( ! isset($_SESSION['__ci_vars']))\n\t\t{\n\t\t\treturn array();\n\t\t}\n\n\t\t$keys = array();\n\t\tforeach (array_keys($_SESSION['__ci_vars']) as $key)\n\t\t{\n\t\t\tis_int($_SESSION['__ci_vars'][$key]) && $keys[] = $key;\n\t\t}\n\n\t\treturn $keys;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Unmark temp\n\t *\n\t * @param\tmixed\t$key\tSession data key(s)\n\t * @return\tvoid\n\t */\n\tpublic function unmark_temp($key)\n\t{\n\t\tif (empty($_SESSION['__ci_vars']))\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tis_array($key) OR $key = array($key);\n\n\t\tforeach ($key as $k)\n\t\t{\n\t\t\tif (isset($_SESSION['__ci_vars'][$k]) && is_int($_SESSION['__ci_vars'][$k]))\n\t\t\t{\n\t\t\t\tunset($_SESSION['__ci_vars'][$k]);\n\t\t\t}\n\t\t}\n\n\t\tif (empty($_SESSION['__ci_vars']))\n\t\t{\n\t\t\tunset($_SESSION['__ci_vars']);\n\t\t}\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * __get()\n\t *\n\t * @param\tstring\t$key\t'session_id' or a session data key\n\t * @return\tmixed\n\t */\n\tpublic function __get($key)\n\t{\n\t\t// Note: Keep this order the same, just in case somebody wants to\n\t\t//       use 'session_id' as a session data key, for whatever reason\n\t\tif (isset($_SESSION[$key]))\n\t\t{\n\t\t\treturn $_SESSION[$key];\n\t\t}\n\t\telseif ($key === 'session_id')\n\t\t{\n\t\t\treturn session_id();\n\t\t}\n\n\t\treturn NULL;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * __isset()\n\t *\n\t * @param\tstring\t$key\t'session_id' or a session data key\n\t * @return\tbool\n\t */\n\tpublic function __isset($key)\n\t{\n\t\tif ($key === 'session_id')\n\t\t{\n\t\t\treturn (session_status() === PHP_SESSION_ACTIVE);\n\t\t}\n\n\t\treturn isset($_SESSION[$key]);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * __set()\n\t *\n\t * @param\tstring\t$key\tSession data key\n\t * @param\tmixed\t$value\tSession data value\n\t * @return\tvoid\n\t */\n\tpublic function __set($key, $value)\n\t{\n\t\t$_SESSION[$key] = $value;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Session destroy\n\t *\n\t * Legacy CI_Session compatibility method\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function sess_destroy()\n\t{\n\t\tsession_destroy();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Session regenerate\n\t *\n\t * Legacy CI_Session compatibility method\n\t *\n\t * @param\tbool\t$destroy\tDestroy old session data flag\n\t * @return\tvoid\n\t */\n\tpublic function sess_regenerate($destroy = FALSE)\n\t{\n\t\t$_SESSION['__ci_last_regenerate'] = time();\n\t\tsession_regenerate_id($destroy);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get userdata reference\n\t *\n\t * Legacy CI_Session compatibility method\n\t *\n\t * @return\tarray\n\t */\n\tpublic function &get_userdata()\n\t{\n\t\treturn $_SESSION;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Userdata (fetch)\n\t *\n\t * Legacy CI_Session compatibility method\n\t *\n\t * @param\tstring\t$key\tSession data key\n\t * @return\tmixed\tSession data value or NULL if not found\n\t */\n\tpublic function userdata($key = NULL)\n\t{\n\t\tif (isset($key))\n\t\t{\n\t\t\treturn isset($_SESSION[$key]) ? $_SESSION[$key] : NULL;\n\t\t}\n\t\telseif (empty($_SESSION))\n\t\t{\n\t\t\treturn array();\n\t\t}\n\n\t\t$userdata = array();\n\t\t$_exclude = array_merge(\n\t\t\tarray('__ci_vars'),\n\t\t\t$this->get_flash_keys(),\n\t\t\t$this->get_temp_keys()\n\t\t);\n\n\t\tforeach (array_keys($_SESSION) as $key)\n\t\t{\n\t\t\tif ( ! in_array($key, $_exclude, TRUE))\n\t\t\t{\n\t\t\t\t$userdata[$key] = $_SESSION[$key];\n\t\t\t}\n\t\t}\n\n\t\treturn $userdata;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Set userdata\n\t *\n\t * Legacy CI_Session compatibility method\n\t *\n\t * @param\tmixed\t$data\tSession data key or an associative array\n\t * @param\tmixed\t$value\tValue to store\n\t * @return\tvoid\n\t */\n\tpublic function set_userdata($data, $value = NULL)\n\t{\n\t\tif (is_array($data))\n\t\t{\n\t\t\tforeach ($data as $key => &$value)\n\t\t\t{\n\t\t\t\t$_SESSION[$key] = $value;\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\t$_SESSION[$data] = $value;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Unset userdata\n\t *\n\t * Legacy CI_Session compatibility method\n\t *\n\t * @param\tmixed\t$key\tSession data key(s)\n\t * @return\tvoid\n\t */\n\tpublic function unset_userdata($key)\n\t{\n\t\tif (is_array($key))\n\t\t{\n\t\t\tforeach ($key as $k)\n\t\t\t{\n\t\t\t\tunset($_SESSION[$k]);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tunset($_SESSION[$key]);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * All userdata (fetch)\n\t *\n\t * Legacy CI_Session compatibility method\n\t *\n\t * @return\tarray\t$_SESSION, excluding flash data items\n\t */\n\tpublic function all_userdata()\n\t{\n\t\treturn $this->userdata();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Has userdata\n\t *\n\t * Legacy CI_Session compatibility method\n\t *\n\t * @param\tstring\t$key\tSession data key\n\t * @return\tbool\n\t */\n\tpublic function has_userdata($key)\n\t{\n\t\treturn isset($_SESSION[$key]);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Flashdata (fetch)\n\t *\n\t * Legacy CI_Session compatibility method\n\t *\n\t * @param\tstring\t$key\tSession data key\n\t * @return\tmixed\tSession data value or NULL if not found\n\t */\n\tpublic function flashdata($key = NULL)\n\t{\n\t\tif (isset($key))\n\t\t{\n\t\t\treturn (isset($_SESSION['__ci_vars'], $_SESSION['__ci_vars'][$key], $_SESSION[$key]) && ! is_int($_SESSION['__ci_vars'][$key]))\n\t\t\t\t? $_SESSION[$key]\n\t\t\t\t: NULL;\n\t\t}\n\n\t\t$flashdata = array();\n\n\t\tif ( ! empty($_SESSION['__ci_vars']))\n\t\t{\n\t\t\tforeach ($_SESSION['__ci_vars'] as $key => &$value)\n\t\t\t{\n\t\t\t\tis_int($value) OR $flashdata[$key] = $_SESSION[$key];\n\t\t\t}\n\t\t}\n\n\t\treturn $flashdata;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Set flashdata\n\t *\n\t * Legacy CI_Session compatibility method\n\t *\n\t * @param\tmixed\t$data\tSession data key or an associative array\n\t * @param\tmixed\t$value\tValue to store\n\t * @return\tvoid\n\t */\n\tpublic function set_flashdata($data, $value = NULL)\n\t{\n\t\t$this->set_userdata($data, $value);\n\t\t$this->mark_as_flash(is_array($data) ? array_keys($data) : $data);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Keep flashdata\n\t *\n\t * Legacy CI_Session compatibility method\n\t *\n\t * @param\tmixed\t$key\tSession data key(s)\n\t * @return\tvoid\n\t */\n\tpublic function keep_flashdata($key)\n\t{\n\t\t$this->mark_as_flash($key);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Temp data (fetch)\n\t *\n\t * Legacy CI_Session compatibility method\n\t *\n\t * @param\tstring\t$key\tSession data key\n\t * @return\tmixed\tSession data value or NULL if not found\n\t */\n\tpublic function tempdata($key = NULL)\n\t{\n\t\tif (isset($key))\n\t\t{\n\t\t\treturn (isset($_SESSION['__ci_vars'], $_SESSION['__ci_vars'][$key], $_SESSION[$key]) && is_int($_SESSION['__ci_vars'][$key]))\n\t\t\t\t? $_SESSION[$key]\n\t\t\t\t: NULL;\n\t\t}\n\n\t\t$tempdata = array();\n\n\t\tif ( ! empty($_SESSION['__ci_vars']))\n\t\t{\n\t\t\tforeach ($_SESSION['__ci_vars'] as $key => &$value)\n\t\t\t{\n\t\t\t\tis_int($value) && $tempdata[$key] = $_SESSION[$key];\n\t\t\t}\n\t\t}\n\n\t\treturn $tempdata;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Set tempdata\n\t *\n\t * Legacy CI_Session compatibility method\n\t *\n\t * @param\tmixed\t$data\tSession data key or an associative array of items\n\t * @param\tmixed\t$value\tValue to store\n\t * @param\tint\t$ttl\tTime-to-live in seconds\n\t * @return\tvoid\n\t */\n\tpublic function set_tempdata($data, $value = NULL, $ttl = 300)\n\t{\n\t\t$this->set_userdata($data, $value);\n\t\t$this->mark_as_temp(is_array($data) ? array_keys($data) : $data, $ttl);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Unset tempdata\n\t *\n\t * Legacy CI_Session compatibility method\n\t *\n\t * @param\tmixed\t$data\tSession data key(s)\n\t * @return\tvoid\n\t */\n\tpublic function unset_tempdata($key)\n\t{\n\t\t$this->unmark_temp($key);\n\t}\n\n}\n"
  },
  {
    "path": "system/libraries/Session/SessionUpdateTimestampHandlerInterface.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * SessionUpdateTimestampHandlerInterface\n *\n * PHP 7 compatibility interface\n *\n * @package\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tSessions\n * @author\tAndrey Andreev\n * @link\thttps://codeigniter.com/userguide3/libraries/sessions.html\n */\ninterface SessionUpdateTimestampHandlerInterface {\n\n\tpublic function updateTimestamp($session_id, $data);\n\tpublic function validateId($session_id);\n}\n"
  },
  {
    "path": "system/libraries/Session/Session_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Session Driver Class\n *\n * @package\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tSessions\n * @author\tAndrey Andreev\n * @link\thttps://codeigniter.com/userguide3/libraries/sessions.html\n */\nabstract class CI_Session_driver {\n\n\tprotected $_config;\n\n\t/**\n\t * Data fingerprint\n\t *\n\t * @var\tbool\n\t */\n\tprotected $_fingerprint;\n\n\t/**\n\t * Lock placeholder\n\t *\n\t * @var\tmixed\n\t */\n\tprotected $_lock = FALSE;\n\n\t/**\n\t * Read session ID\n\t *\n\t * Used to detect session_regenerate_id() calls because PHP only calls\n\t * write() after regenerating the ID.\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_session_id;\n\n\t/**\n\t * Success and failure return values\n\t *\n\t * Necessary due to a bug in all PHP 5 versions where return values\n\t * from userspace handlers are not handled properly. PHP 7 fixes the\n\t * bug, so we need to return different values depending on the version.\n\t *\n\t * @see\thttps://wiki.php.net/rfc/session.user.return-value\n\t * @var\tmixed\n\t */\n\tprotected $_success, $_failure;\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * @param\tarray\t$params\tConfiguration parameters\n\t * @return\tvoid\n\t */\n\tpublic function __construct(&$params)\n\t{\n\t\t$this->_config =& $params;\n\n\t\tif (is_php('7'))\n\t\t{\n\t\t\t$this->_success = TRUE;\n\t\t\t$this->_failure = FALSE;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$this->_success = 0;\n\t\t\t$this->_failure = -1;\n\t\t}\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * PHP 5.x validate ID\n\t *\n\t * Enforces session.use_strict_mode\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function php5_validate_id()\n\t{\n\t\tif ($this->_success === 0 && isset($_COOKIE[$this->_config['cookie_name']]) && ! $this->validateId($_COOKIE[$this->_config['cookie_name']]))\n\t\t{\n\t\t\tunset($_COOKIE[$this->_config['cookie_name']]);\n\t\t}\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Cookie destroy\n\t *\n\t * Internal method to force removal of a cookie by the client\n\t * when session_destroy() is called.\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _cookie_destroy()\n\t{\n\t\tif ( ! is_php('7.3'))\n\t\t{\n\t\t\t$header = 'Set-Cookie: '.$this->_config['cookie_name'].'=';\n\t\t\t$header .= '; Expires='.gmdate('D, d-M-Y H:i:s T', 1).'; Max-Age=-1';\n\t\t\t$header .= '; Path='.$this->_config['cookie_path'];\n\t\t\t$header .= ($this->_config['cookie_domain'] !== '' ? '; Domain='.$this->_config['cookie_domain'] : '');\n\t\t\t$header .= ($this->_config['cookie_secure'] ? '; Secure' : '').'; HttpOnly; SameSite='.$this->_config['cookie_samesite'];\n\t\t\theader($header);\n\t\t\treturn;\n\t\t}\n\n\t\treturn setcookie(\n\t\t\t$this->_config['cookie_name'],\n\t\t\t'',\n\t\t\tarray(\n\t\t\t\t'expires' => 1,\n\t\t\t\t'path' => $this->_config['cookie_path'],\n\t\t\t\t'domain' => $this->_config['cookie_domain'],\n\t\t\t\t'secure' => $this->_config['cookie_secure'],\n\t\t\t\t'httponly' => TRUE,\n\t\t\t\t'samesite' => $this->_config['cookie_samesite']\n\t\t\t)\n\t\t);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get lock\n\t *\n\t * A dummy method allowing drivers with no locking functionality\n\t * (databases other than PostgreSQL and MySQL) to act as if they\n\t * do acquire a lock.\n\t *\n\t * @param\tstring\t$session_id\n\t * @return\tbool\n\t */\n\tprotected function _get_lock($session_id)\n\t{\n\t\t$this->_lock = TRUE;\n\t\treturn TRUE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Release lock\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _release_lock()\n\t{\n\t\tif ($this->_lock)\n\t\t{\n\t\t\t$this->_lock = FALSE;\n\t\t}\n\n\t\treturn TRUE;\n\t}\n}\n"
  },
  {
    "path": "system/libraries/Session/drivers/Session_database_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Session Database Driver\n *\n * @package\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tSessions\n * @author\tAndrey Andreev\n * @link\thttps://codeigniter.com/userguide3/libraries/sessions.html\n */\nclass CI_Session_database_driver extends CI_Session_driver implements CI_Session_driver_interface {\n\n\t/**\n\t * DB object\n\t *\n\t * @var\tobject\n\t */\n\tprotected $_db;\n\n\t/**\n\t * Row exists flag\n\t *\n\t * @var\tbool\n\t */\n\tprotected $_row_exists = FALSE;\n\n\t/**\n\t * Lock \"driver\" flag\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_platform;\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * @param\tarray\t$params\tConfiguration parameters\n\t * @return\tvoid\n\t */\n\tpublic function __construct(&$params)\n\t{\n\t\tparent::__construct($params);\n\n\t\t$CI =& get_instance();\n\t\tisset($CI->db) OR $CI->load->database();\n\t\t$this->_db = $CI->db;\n\n\t\tif ( ! $this->_db instanceof CI_DB_query_builder)\n\t\t{\n\t\t\tthrow new Exception('Query Builder not enabled for the configured database. Aborting.');\n\t\t}\n\t\telseif ($this->_db->pconnect)\n\t\t{\n\t\t\tthrow new Exception('Configured database connection is persistent. Aborting.');\n\t\t}\n\t\telseif ($this->_db->cache_on)\n\t\t{\n\t\t\tthrow new Exception('Configured database connection has cache enabled. Aborting.');\n\t\t}\n\n\t\t$db_driver = $this->_db->dbdriver.(empty($this->_db->subdriver) ? '' : '_'.$this->_db->subdriver);\n\t\tif (strpos($db_driver, 'mysql') !== FALSE)\n\t\t{\n\t\t\t$this->_platform = 'mysql';\n\t\t}\n\t\telseif (in_array($db_driver, array('postgre', 'pdo_pgsql'), TRUE))\n\t\t{\n\t\t\t$this->_platform = 'postgre';\n\t\t}\n\n\t\t// Note: BC work-around for the old 'sess_table_name' setting, should be removed in the future.\n\t\tif ( ! isset($this->_config['save_path']) && ($this->_config['save_path'] = config_item('sess_table_name')))\n\t\t{\n\t\t\tlog_message('debug', 'Session: \"sess_save_path\" is empty; using BC fallback to \"sess_table_name\".');\n\t\t}\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Open\n\t *\n\t * Initializes the database connection\n\t *\n\t * @param\tstring\t$save_path\tTable name\n\t * @param\tstring\t$name\t\tSession cookie name, unused\n\t * @return\tbool\n\t */\n\tpublic function open($save_path, $name)\n\t{\n\t\tif (empty($this->_db->conn_id) && ! $this->_db->db_connect())\n\t\t{\n\t\t\treturn $this->_failure;\n\t\t}\n\n\t\t$this->php5_validate_id();\n\n\t\treturn $this->_success;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Read\n\t *\n\t * Reads session data and acquires a lock\n\t *\n\t * @param\tstring\t$session_id\tSession ID\n\t * @return\tstring\tSerialized session data\n\t */\n\tpublic function read($session_id)\n\t{\n\t\tif ($this->_get_lock($session_id) === FALSE)\n\t\t{\n\t\t\treturn $this->_failure;\n\t\t}\n\n\t\t// Prevent previous QB calls from messing with our queries\n\t\t$this->_db->reset_query();\n\n\t\t// Needed by write() to detect session_regenerate_id() calls\n\t\t$this->_session_id = $session_id;\n\n\t\t$this->_db\n\t\t\t->select('data')\n\t\t\t->from($this->_config['save_path'])\n\t\t\t->where('id', $session_id);\n\n\t\tif ($this->_config['match_ip'])\n\t\t{\n\t\t\t$this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']);\n\t\t}\n\n\t\tif ( ! ($result = $this->_db->get()) OR ($result = $result->row()) === NULL)\n\t\t{\n\t\t\t// PHP7 will reuse the same SessionHandler object after\n\t\t\t// ID regeneration, so we need to explicitly set this to\n\t\t\t// FALSE instead of relying on the default ...\n\t\t\t$this->_row_exists = FALSE;\n\t\t\t$this->_fingerprint = md5('');\n\t\t\treturn '';\n\t\t}\n\n\t\t// PostgreSQL's variant of a BLOB datatype is Bytea, which is a\n\t\t// PITA to work with, so we use base64-encoded data in a TEXT\n\t\t// field instead.\n\t\t$result = ($this->_platform === 'postgre')\n\t\t\t? base64_decode(rtrim($result->data))\n\t\t\t: $result->data;\n\n\t\t$this->_fingerprint = md5($result);\n\t\t$this->_row_exists = TRUE;\n\t\treturn $result;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Write\n\t *\n\t * Writes (create / update) session data\n\t *\n\t * @param\tstring\t$session_id\tSession ID\n\t * @param\tstring\t$session_data\tSerialized session data\n\t * @return\tbool\n\t */\n\tpublic function write($session_id, $session_data)\n\t{\n\t\t// Prevent previous QB calls from messing with our queries\n\t\t$this->_db->reset_query();\n\n\t\t// Was the ID regenerated?\n\t\tif (isset($this->_session_id) && $session_id !== $this->_session_id)\n\t\t{\n\t\t\tif ( ! $this->_release_lock() OR ! $this->_get_lock($session_id))\n\t\t\t{\n\t\t\t\treturn $this->_failure;\n\t\t\t}\n\n\t\t\t$this->_row_exists = FALSE;\n\t\t\t$this->_session_id = $session_id;\n\t\t}\n\t\telseif ($this->_lock === FALSE)\n\t\t{\n\t\t\treturn $this->_failure;\n\t\t}\n\n\t\tif ($this->_row_exists === FALSE)\n\t\t{\n\t\t\t$insert_data = array(\n\t\t\t\t'id' => $session_id,\n\t\t\t\t'ip_address' => $_SERVER['REMOTE_ADDR'],\n\t\t\t\t'timestamp' => time(),\n\t\t\t\t'data' => ($this->_platform === 'postgre' ? base64_encode($session_data) : $session_data)\n\t\t\t);\n\n\t\t\tif ($this->_db->insert($this->_config['save_path'], $insert_data))\n\t\t\t{\n\t\t\t\t$this->_fingerprint = md5($session_data);\n\t\t\t\t$this->_row_exists = TRUE;\n\t\t\t\treturn $this->_success;\n\t\t\t}\n\n\t\t\treturn $this->_failure;\n\t\t}\n\n\t\t$this->_db->where('id', $session_id);\n\t\tif ($this->_config['match_ip'])\n\t\t{\n\t\t\t$this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']);\n\t\t}\n\n\t\t$update_data = array('timestamp' => time());\n\t\tif ($this->_fingerprint !== md5($session_data))\n\t\t{\n\t\t\t$update_data['data'] = ($this->_platform === 'postgre')\n\t\t\t\t? base64_encode($session_data)\n\t\t\t\t: $session_data;\n\t\t}\n\n\t\tif ($this->_db->update($this->_config['save_path'], $update_data))\n\t\t{\n\t\t\t$this->_fingerprint = md5($session_data);\n\t\t\treturn $this->_success;\n\t\t}\n\n\t\treturn $this->_failure;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Close\n\t *\n\t * Releases locks\n\t *\n\t * @return\tbool\n\t */\n\tpublic function close()\n\t{\n\t\treturn ($this->_lock && ! $this->_release_lock())\n\t\t\t? $this->_failure\n\t\t\t: $this->_success;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Destroy\n\t *\n\t * Destroys the current session.\n\t *\n\t * @param\tstring\t$session_id\tSession ID\n\t * @return\tbool\n\t */\n\tpublic function destroy($session_id)\n\t{\n\t\tif ($this->_lock)\n\t\t{\n\t\t\t// Prevent previous QB calls from messing with our queries\n\t\t\t$this->_db->reset_query();\n\n\t\t\t$this->_db->where('id', $session_id);\n\t\t\tif ($this->_config['match_ip'])\n\t\t\t{\n\t\t\t\t$this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']);\n\t\t\t}\n\n\t\t\tif ( ! $this->_db->delete($this->_config['save_path']))\n\t\t\t{\n\t\t\t\treturn $this->_failure;\n\t\t\t}\n\t\t}\n\n\t\tif ($this->close() === $this->_success)\n\t\t{\n\t\t\t$this->_cookie_destroy();\n\t\t\treturn $this->_success;\n\t\t}\n\n\t\treturn $this->_failure;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Garbage Collector\n\t *\n\t * Deletes expired sessions\n\t *\n\t * @param\tint \t$maxlifetime\tMaximum lifetime of sessions\n\t * @return\tbool\n\t */\n\tpublic function gc($maxlifetime)\n\t{\n\t\t// Prevent previous QB calls from messing with our queries\n\t\t$this->_db->reset_query();\n\n\t\treturn ($this->_db->delete($this->_config['save_path'], 'timestamp < '.(time() - $maxlifetime)))\n\t\t\t? $this->_success\n\t\t\t: $this->_failure;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Update Timestamp\n\t *\n\t * Update session timestamp without modifying data\n\t *\n\t * @param\tstring\t$id\tSession ID\n\t * @param\tstring\t$data\tUnknown & unused\n\t * @return\tbool\n\t */\n\tpublic function updateTimestamp($id, $unknown)\n\t{\n\t\t// Prevent previous QB calls from messing with our queries\n\t\t$this->_db->reset_query();\n\n\t\t$this->_db->where('id', $id);\n\t\tif ($this->_config['match_ip'])\n\t\t{\n\t\t\t$this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']);\n\t\t}\n\n\t\treturn (bool) $this->_db->update($this->_config['save_path'], array('timestamp' => time()));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Validate ID\n\t *\n\t * Checks whether a session ID record exists server-side,\n\t * to enforce session.use_strict_mode.\n\t *\n\t * @param\tstring\t$id\tSession ID\n\t * @return\tbool\n\t */\n\tpublic function validateId($id)\n\t{\n\t\t// Prevent previous QB calls from messing with our queries\n\t\t$this->_db->reset_query();\n\n\t\t$this->_db->select('1')->from($this->_config['save_path'])->where('id', $id);\n\t\tempty($this->_config['match_ip']) OR $this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']);\n\t\t$result = $this->_db->get();\n\t\tempty($result) OR $result = $result->row();\n\n\t\treturn ! empty($result);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get lock\n\t *\n\t * Acquires a lock, depending on the underlying platform.\n\t *\n\t * @param\tstring\t$session_id\tSession ID\n\t * @return\tbool\n\t */\n\tprotected function _get_lock($session_id)\n\t{\n\t\tif ($this->_platform === 'mysql')\n\t\t{\n\t\t\t$arg = md5($session_id.($this->_config['match_ip'] ? '_'.$_SERVER['REMOTE_ADDR'] : ''));\n\t\t\tif ($this->_db->query(\"SELECT GET_LOCK('\".$arg.\"', 300) AS ci_session_lock\")->row()->ci_session_lock)\n\t\t\t{\n\t\t\t\t$this->_lock = $arg;\n\t\t\t\treturn TRUE;\n\t\t\t}\n\n\t\t\treturn FALSE;\n\t\t}\n\t\telseif ($this->_platform === 'postgre')\n\t\t{\n\t\t\t$arg = \"hashtext('\".$session_id.\"')\".($this->_config['match_ip'] ? \", hashtext('\".$_SERVER['REMOTE_ADDR'].\"')\" : '');\n\t\t\tif ($this->_db->simple_query('SELECT pg_advisory_lock('.$arg.')'))\n\t\t\t{\n\t\t\t\t$this->_lock = $arg;\n\t\t\t\treturn TRUE;\n\t\t\t}\n\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn parent::_get_lock($session_id);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Release lock\n\t *\n\t * Releases a previously acquired lock\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _release_lock()\n\t{\n\t\tif ( ! $this->_lock)\n\t\t{\n\t\t\treturn TRUE;\n\t\t}\n\n\t\tif ($this->_platform === 'mysql')\n\t\t{\n\t\t\tif ($this->_db->query(\"SELECT RELEASE_LOCK('\".$this->_lock.\"') AS ci_session_lock\")->row()->ci_session_lock)\n\t\t\t{\n\t\t\t\t$this->_lock = FALSE;\n\t\t\t\treturn TRUE;\n\t\t\t}\n\n\t\t\treturn FALSE;\n\t\t}\n\t\telseif ($this->_platform === 'postgre')\n\t\t{\n\t\t\tif ($this->_db->simple_query('SELECT pg_advisory_unlock('.$this->_lock.')'))\n\t\t\t{\n\t\t\t\t$this->_lock = FALSE;\n\t\t\t\treturn TRUE;\n\t\t\t}\n\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn parent::_release_lock();\n\t}\n}\n"
  },
  {
    "path": "system/libraries/Session/drivers/Session_files_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Session Files Driver\n *\n * @package\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tSessions\n * @author\tAndrey Andreev\n * @link\thttps://codeigniter.com/userguide3/libraries/sessions.html\n */\nclass CI_Session_files_driver extends CI_Session_driver implements CI_Session_driver_interface {\n\n\t/**\n\t * Save path\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_save_path;\n\n\t/**\n\t * File handle\n\t *\n\t * @var\tresource\n\t */\n\tprotected $_file_handle;\n\n\t/**\n\t * File name\n\t *\n\t * @var\tresource\n\t */\n\tprotected $_file_path;\n\n\t/**\n\t * File new flag\n\t *\n\t * @var\tbool\n\t */\n\tprotected $_file_new;\n\n\t/**\n\t * Validate SID regular expression\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_sid_regexp;\n\n\t/**\n\t * mbstring.func_overload flag\n\t *\n\t * @var\tbool\n\t */\n\tprotected static $func_overload;\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * @param\tarray\t$params\tConfiguration parameters\n\t * @return\tvoid\n\t */\n\tpublic function __construct(&$params)\n\t{\n\t\tparent::__construct($params);\n\n\t\tif (isset($this->_config['save_path']))\n\t\t{\n\t\t\t$this->_config['save_path'] = rtrim($this->_config['save_path'], '/\\\\');\n\t\t\tini_set('session.save_path', $this->_config['save_path']);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlog_message('debug', 'Session: \"sess_save_path\" is empty; using \"session.save_path\" value from php.ini.');\n\t\t\t$this->_config['save_path'] = rtrim(ini_get('session.save_path'), '/\\\\');\n\t\t}\n\n\t\t$this->_sid_regexp = $this->_config['_sid_regexp'];\n\n\t\tisset(self::$func_overload) OR self::$func_overload = ( ! is_php('8.0') && extension_loaded('mbstring') && @ini_get('mbstring.func_overload'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Open\n\t *\n\t * Sanitizes the save_path directory.\n\t *\n\t * @param\tstring\t$save_path\tPath to session files' directory\n\t * @param\tstring\t$name\t\tSession cookie name\n\t * @return\tbool\n\t */\n\tpublic function open($save_path, $name)\n\t{\n\t\tif ( ! is_dir($save_path))\n\t\t{\n\t\t\tif ( ! mkdir($save_path, 0700, TRUE))\n\t\t\t{\n\t\t\t\tlog_message('error', \"Session: Configured save path '\".$this->_config['save_path'].\"' is not a directory, doesn't exist or cannot be created.\");\n\t\t\t\treturn $this->_failure;\n\t\t\t}\n\t\t}\n\t\telseif ( ! is_writable($save_path))\n\t\t{\n\t\t\tlog_message('error', \"Session: Configured save path '\".$this->_config['save_path'].\"' is not writable by the PHP process.\");\n\t\t\treturn $this->_failure;\n\t\t}\n\n\t\t$this->_config['save_path'] = $save_path;\n\t\t$this->_file_path = $this->_config['save_path'].DIRECTORY_SEPARATOR\n\t\t\t.$name // we'll use the session cookie name as a prefix to avoid collisions\n\t\t\t.($this->_config['match_ip'] ? md5($_SERVER['REMOTE_ADDR']) : '');\n\n\t\t$this->php5_validate_id();\n\n\t\treturn $this->_success;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Read\n\t *\n\t * Reads session data and acquires a lock\n\t *\n\t * @param\tstring\t$session_id\tSession ID\n\t * @return\tstring\tSerialized session data\n\t */\n\tpublic function read($session_id)\n\t{\n\t\t// This might seem weird, but PHP 5.6 introduces session_reset(),\n\t\t// which re-reads session data\n\t\tif ($this->_file_handle === NULL)\n\t\t{\n\t\t\t$this->_file_new = ! file_exists($this->_file_path.$session_id);\n\n\t\t\tif (($this->_file_handle = fopen($this->_file_path.$session_id, 'c+b')) === FALSE)\n\t\t\t{\n\t\t\t\tlog_message('error', \"Session: Unable to open file '\".$this->_file_path.$session_id.\"'.\");\n\t\t\t\treturn $this->_failure;\n\t\t\t}\n\n\t\t\tif (flock($this->_file_handle, LOCK_EX) === FALSE)\n\t\t\t{\n\t\t\t\tlog_message('error', \"Session: Unable to obtain lock for file '\".$this->_file_path.$session_id.\"'.\");\n\t\t\t\tfclose($this->_file_handle);\n\t\t\t\t$this->_file_handle = NULL;\n\t\t\t\treturn $this->_failure;\n\t\t\t}\n\n\t\t\t// Needed by write() to detect session_regenerate_id() calls\n\t\t\t$this->_session_id = $session_id;\n\n\t\t\tif ($this->_file_new)\n\t\t\t{\n\t\t\t\tchmod($this->_file_path.$session_id, 0600);\n\t\t\t\t$this->_fingerprint = md5('');\n\t\t\t\treturn '';\n\t\t\t}\n\n\t\t\t// Prevent possible data corruption\n\t\t\t// See https://github.com/bcit-ci/CodeIgniter/issues/5857\n\t\t\tclearstatcache(TRUE, $this->_file_path.$session_id);\n\t\t}\n\t\t// We shouldn't need this, but apparently we do ...\n\t\t// See https://github.com/bcit-ci/CodeIgniter/issues/4039\n\t\telseif ($this->_file_handle === FALSE)\n\t\t{\n\t\t\treturn $this->_failure;\n\t\t}\n\t\telse\n\t\t{\n\t\t\trewind($this->_file_handle);\n\t\t}\n\n\t\t$session_data = '';\n\t\tfor ($read = 0, $length = filesize($this->_file_path.$session_id); $read < $length; $read += self::strlen($buffer))\n\t\t{\n\t\t\tif (($buffer = fread($this->_file_handle, $length - $read)) === FALSE)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t$session_data .= $buffer;\n\t\t}\n\n\t\t$this->_fingerprint = md5($session_data);\n\t\treturn $session_data;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Write\n\t *\n\t * Writes (create / update) session data\n\t *\n\t * @param\tstring\t$session_id\tSession ID\n\t * @param\tstring\t$session_data\tSerialized session data\n\t * @return\tbool\n\t */\n\tpublic function write($session_id, $session_data)\n\t{\n\t\t// If the two IDs don't match, we have a session_regenerate_id() call\n\t\t// and we need to close the old handle and open a new one\n\t\tif ($session_id !== $this->_session_id && ($this->close() === $this->_failure OR $this->read($session_id) === $this->_failure))\n\t\t{\n\t\t\treturn $this->_failure;\n\t\t}\n\n\t\tif ( ! is_resource($this->_file_handle))\n\t\t{\n\t\t\treturn $this->_failure;\n\t\t}\n\t\telseif ($this->_fingerprint === md5($session_data))\n\t\t{\n\t\t\treturn ( ! $this->_file_new && ! touch($this->_file_path.$session_id))\n\t\t\t\t? $this->_failure\n\t\t\t\t: $this->_success;\n\t\t}\n\n\t\tif ( ! $this->_file_new)\n\t\t{\n\t\t\tftruncate($this->_file_handle, 0);\n\t\t\trewind($this->_file_handle);\n\t\t}\n\n\t\tif (($length = strlen($session_data)) > 0)\n\t\t{\n\t\t\tfor ($written = 0; $written < $length; $written += $result)\n\t\t\t{\n\t\t\t\tif (($result = fwrite($this->_file_handle, substr($session_data, $written))) === FALSE)\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( ! is_int($result))\n\t\t\t{\n\t\t\t\t$this->_fingerprint = md5(substr($session_data, 0, $written));\n\t\t\t\tlog_message('error', 'Session: Unable to write data.');\n\t\t\t\treturn $this->_failure;\n\t\t\t}\n\t\t}\n\n\t\t$this->_fingerprint = md5($session_data);\n\t\treturn $this->_success;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Close\n\t *\n\t * Releases locks and closes file descriptor.\n\t *\n\t * @return\tbool\n\t */\n\tpublic function close()\n\t{\n\t\tif (is_resource($this->_file_handle))\n\t\t{\n\t\t\tflock($this->_file_handle, LOCK_UN);\n\t\t\tfclose($this->_file_handle);\n\n\t\t\t$this->_file_handle = $this->_file_new = $this->_session_id = NULL;\n\t\t}\n\n\t\treturn $this->_success;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Destroy\n\t *\n\t * Destroys the current session.\n\t *\n\t * @param\tstring\t$session_id\tSession ID\n\t * @return\tbool\n\t */\n\tpublic function destroy($session_id)\n\t{\n\t\tif ($this->close() === $this->_success)\n\t\t{\n\t\t\tif (file_exists($this->_file_path.$session_id))\n\t\t\t{\n\t\t\t\t$this->_cookie_destroy();\n\t\t\t\treturn unlink($this->_file_path.$session_id)\n\t\t\t\t\t? $this->_success\n\t\t\t\t\t: $this->_failure;\n\t\t\t}\n\n\t\t\treturn $this->_success;\n\t\t}\n\t\telseif ($this->_file_path !== NULL)\n\t\t{\n\t\t\tclearstatcache();\n\t\t\tif (file_exists($this->_file_path.$session_id))\n\t\t\t{\n\t\t\t\t$this->_cookie_destroy();\n\t\t\t\treturn unlink($this->_file_path.$session_id)\n\t\t\t\t\t? $this->_success\n\t\t\t\t\t: $this->_failure;\n\t\t\t}\n\n\t\t\treturn $this->_success;\n\t\t}\n\n\t\treturn $this->_failure;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Garbage Collector\n\t *\n\t * Deletes expired sessions\n\t *\n\t * @param\tint \t$maxlifetime\tMaximum lifetime of sessions\n\t * @return\tbool\n\t */\n\tpublic function gc($maxlifetime)\n\t{\n\t\tif ( ! is_dir($this->_config['save_path']) OR ($directory = opendir($this->_config['save_path'])) === FALSE)\n\t\t{\n\t\t\tlog_message('debug', \"Session: Garbage collector couldn't list files under directory '\".$this->_config['save_path'].\"'.\");\n\t\t\treturn $this->_failure;\n\t\t}\n\n\t\t$ts = time() - $maxlifetime;\n\n\t\t$pattern = ($this->_config['match_ip'] === TRUE)\n\t\t\t? '[0-9a-f]{32}'\n\t\t\t: '';\n\n\t\t$pattern = sprintf(\n\t\t\t'#\\A%s'.$pattern.$this->_sid_regexp.'\\z#',\n\t\t\tpreg_quote($this->_config['cookie_name'])\n\t\t);\n\n\t\twhile (($file = readdir($directory)) !== FALSE)\n\t\t{\n\t\t\t// If the filename doesn't match this pattern, it's either not a session file or is not ours\n\t\t\tif ( ! preg_match($pattern, $file)\n\t\t\t\tOR ! is_file($this->_config['save_path'].DIRECTORY_SEPARATOR.$file)\n\t\t\t\tOR ($mtime = filemtime($this->_config['save_path'].DIRECTORY_SEPARATOR.$file)) === FALSE\n\t\t\t\tOR $mtime > $ts)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tunlink($this->_config['save_path'].DIRECTORY_SEPARATOR.$file);\n\t\t}\n\n\t\tclosedir($directory);\n\n\t\treturn $this->_success;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Update Timestamp\n\t *\n\t * Update session timestamp without modifying data\n\t *\n\t * @param\tstring\t$id\tSession ID\n\t * @param\tstring\t$data\tUnknown & unused\n\t * @return\tbool\n\t */\n\tpublic function updateTimestamp($id, $unknown)\n\t{\n\t\treturn touch($this->_file_path.$id);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Validate ID\n\t *\n\t * Checks whether a session ID record exists server-side,\n\t * to enforce session.use_strict_mode.\n\t *\n\t * @param\tstring\t$id\tSession ID\n\t * @return\tbool\n\t */\n\tpublic function validateId($id)\n\t{\n\t\t$result = is_file($this->_file_path.$id);\n\t\tclearstatcache(TRUE, $this->_file_path.$id);\n\t\treturn $result;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Byte-safe strlen()\n\t *\n\t * @param\tstring\t$str\n\t * @return\tint\n\t */\n\tprotected static function strlen($str)\n\t{\n\t\treturn (self::$func_overload)\n\t\t\t? mb_strlen($str, '8bit')\n\t\t\t: strlen($str);\n\t}\n}\n"
  },
  {
    "path": "system/libraries/Session/drivers/Session_memcached_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Session Memcached Driver\n *\n * @package\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tSessions\n * @author\tAndrey Andreev\n * @link\thttps://codeigniter.com/userguide3/libraries/sessions.html\n */\nclass CI_Session_memcached_driver extends CI_Session_driver implements CI_Session_driver_interface {\n\n\t/**\n\t * Memcached instance\n\t *\n\t * @var\tMemcached\n\t */\n\tprotected $_memcached;\n\n\t/**\n\t * Key prefix\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_key_prefix = 'ci_session:';\n\n\t/**\n\t * Lock key\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_lock_key;\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * @param\tarray\t$params\tConfiguration parameters\n\t * @return\tvoid\n\t */\n\tpublic function __construct(&$params)\n\t{\n\t\tparent::__construct($params);\n\n\t\tif (empty($this->_config['save_path']))\n\t\t{\n\t\t\tlog_message('error', 'Session: No Memcached save path configured.');\n\t\t}\n\n\t\tif ($this->_config['match_ip'] === TRUE)\n\t\t{\n\t\t\t$this->_key_prefix .= $_SERVER['REMOTE_ADDR'].':';\n\t\t}\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Open\n\t *\n\t * Sanitizes save_path and initializes connections.\n\t *\n\t * @param\tstring\t$save_path\tServer path(s)\n\t * @param\tstring\t$name\t\tSession cookie name, unused\n\t * @return\tbool\n\t */\n\tpublic function open($save_path, $name)\n\t{\n\t\t$this->_memcached = new Memcached();\n\t\t$this->_memcached->setOption(Memcached::OPT_BINARY_PROTOCOL, TRUE); // required for touch() usage\n\t\t$server_list = array();\n\t\tforeach ($this->_memcached->getServerList() as $server)\n\t\t{\n\t\t\t$server_list[] = $server['host'].':'.$server['port'];\n\t\t}\n\n\t\tif ( ! preg_match_all('#,?([^,:]+)\\:(\\d{1,5})(?:\\:(\\d+))?#', $this->_config['save_path'], $matches, PREG_SET_ORDER))\n\t\t{\n\t\t\t$this->_memcached = NULL;\n\t\t\tlog_message('error', 'Session: Invalid Memcached save path format: '.$this->_config['save_path']);\n\t\t\treturn $this->_failure;\n\t\t}\n\n\t\tforeach ($matches as $match)\n\t\t{\n\t\t\t// If Memcached already has this server (or if the port is invalid), skip it\n\t\t\tif (in_array($match[1].':'.$match[2], $server_list, TRUE))\n\t\t\t{\n\t\t\t\tlog_message('debug', 'Session: Memcached server pool already has '.$match[1].':'.$match[2]);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ( ! $this->_memcached->addServer($match[1], $match[2], isset($match[3]) ? $match[3] : 0))\n\t\t\t{\n\t\t\t\tlog_message('error', 'Could not add '.$match[1].':'.$match[2].' to Memcached server pool.');\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$server_list[] = $match[1].':'.$match[2];\n\t\t\t}\n\t\t}\n\n\t\tif (empty($server_list))\n\t\t{\n\t\t\tlog_message('error', 'Session: Memcached server pool is empty.');\n\t\t\treturn $this->_failure;\n\t\t}\n\n\t\t$this->php5_validate_id();\n\n\t\treturn $this->_success;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Read\n\t *\n\t * Reads session data and acquires a lock\n\t *\n\t * @param\tstring\t$session_id\tSession ID\n\t * @return\tstring\tSerialized session data\n\t */\n\tpublic function read($session_id)\n\t{\n\t\tif (isset($this->_memcached) && $this->_get_lock($session_id))\n\t\t{\n\t\t\t// Needed by write() to detect session_regenerate_id() calls\n\t\t\t$this->_session_id = $session_id;\n\n\t\t\t$session_data = (string) $this->_memcached->get($this->_key_prefix.$session_id);\n\t\t\t$this->_fingerprint = md5($session_data);\n\t\t\treturn $session_data;\n\t\t}\n\n\t\treturn $this->_failure;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Write\n\t *\n\t * Writes (create / update) session data\n\t *\n\t * @param\tstring\t$session_id\tSession ID\n\t * @param\tstring\t$session_data\tSerialized session data\n\t * @return\tbool\n\t */\n\tpublic function write($session_id, $session_data)\n\t{\n\t\tif ( ! isset($this->_memcached, $this->_lock_key))\n\t\t{\n\t\t\treturn $this->_failure;\n\t\t}\n\t\t// Was the ID regenerated?\n\t\telseif ($session_id !== $this->_session_id)\n\t\t{\n\t\t\tif ( ! $this->_release_lock() OR ! $this->_get_lock($session_id))\n\t\t\t{\n\t\t\t\treturn $this->_failure;\n\t\t\t}\n\n\t\t\t$this->_fingerprint = md5('');\n\t\t\t$this->_session_id = $session_id;\n\t\t}\n\n\t\t$key = $this->_key_prefix.$session_id;\n\n\t\t$this->_memcached->replace($this->_lock_key, time(), 300);\n\t\tif ($this->_fingerprint !== ($fingerprint = md5($session_data)))\n\t\t{\n\t\t\tif ($this->_memcached->set($key, $session_data, $this->_config['expiration']))\n\t\t\t{\n\t\t\t\t$this->_fingerprint = $fingerprint;\n\t\t\t\treturn $this->_success;\n\t\t\t}\n\n\t\t\treturn $this->_failure;\n\t\t}\n\t\telseif (\n\t\t\t$this->_memcached->touch($key, $this->_config['expiration'])\n\t\t\tOR ($this->_memcached->getResultCode() === Memcached::RES_NOTFOUND && $this->_memcached->set($key, $session_data, $this->_config['expiration']))\n\t\t)\n\t\t{\n\t\t\treturn $this->_success;\n\t\t}\n\n\t\treturn $this->_failure;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Close\n\t *\n\t * Releases locks and closes connection.\n\t *\n\t * @return\tbool\n\t */\n\tpublic function close()\n\t{\n\t\tif (isset($this->_memcached))\n\t\t{\n\t\t\t$this->_release_lock();\n\t\t\tif ( ! $this->_memcached->quit())\n\t\t\t{\n\t\t\t\treturn $this->_failure;\n\t\t\t}\n\n\t\t\t$this->_memcached = NULL;\n\t\t\treturn $this->_success;\n\t\t}\n\n\t\treturn $this->_failure;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Destroy\n\t *\n\t * Destroys the current session.\n\t *\n\t * @param\tstring\t$session_id\tSession ID\n\t * @return\tbool\n\t */\n\tpublic function destroy($session_id)\n\t{\n\t\tif (isset($this->_memcached, $this->_lock_key))\n\t\t{\n\t\t\t$this->_memcached->delete($this->_key_prefix.$session_id);\n\t\t\t$this->_cookie_destroy();\n\t\t\treturn $this->_success;\n\t\t}\n\n\t\treturn $this->_failure;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Garbage Collector\n\t *\n\t * Deletes expired sessions\n\t *\n\t * @param\tint \t$maxlifetime\tMaximum lifetime of sessions\n\t * @return\tbool\n\t */\n\tpublic function gc($maxlifetime)\n\t{\n\t\t// Not necessary, Memcached takes care of that.\n\t\treturn $this->_success;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Update Timestamp\n\t *\n\t * Update session timestamp without modifying data\n\t *\n\t * @param\tstring\t$id\tSession ID\n\t * @param\tstring\t$data\tUnknown & unused\n\t * @return\tbool\n\t */\n\tpublic function updateTimestamp($id, $unknown)\n\t{\n\t\treturn $this->_memcached->touch($this->_key_prefix.$id, $this->_config['expiration']);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Validate ID\n\t *\n\t * Checks whether a session ID record exists server-side,\n\t * to enforce session.use_strict_mode.\n\t *\n\t * @param\tstring\t$id\tSession ID\n\t * @return\tbool\n\t */\n\tpublic function validateId($id)\n\t{\n\t\t$this->_memcached->get($this->_key_prefix.$id);\n\t\treturn ($this->_memcached->getResultCode() === Memcached::RES_SUCCESS);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get lock\n\t *\n\t * Acquires an (emulated) lock.\n\t *\n\t * @param\tstring\t$session_id\tSession ID\n\t * @return\tbool\n\t */\n\tprotected function _get_lock($session_id)\n\t{\n\t\t// PHP 7 reuses the SessionHandler object on regeneration,\n\t\t// so we need to check here if the lock key is for the\n\t\t// correct session ID.\n\t\tif ($this->_lock_key === $this->_key_prefix.$session_id.':lock')\n\t\t{\n\t\t\tif ( ! $this->_memcached->replace($this->_lock_key, time(), 300))\n\t\t\t{\n\t\t\t\treturn ($this->_memcached->getResultCode() === Memcached::RES_NOTFOUND)\n\t\t\t\t\t? $this->_memcached->add($this->_lock_key, time(), 300)\n\t\t\t\t\t: FALSE;\n\t\t\t}\n\n\t\t\treturn TRUE;\n\t\t}\n\n\t\t// 30 attempts to obtain a lock, in case another request already has it\n\t\t$lock_key = $this->_key_prefix.$session_id.':lock';\n\t\t$attempt = 0;\n\t\tdo\n\t\t{\n\t\t\tif ($this->_memcached->get($lock_key))\n\t\t\t{\n\t\t\t\tsleep(1);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$method = ($this->_memcached->getResultCode() === Memcached::RES_NOTFOUND) ? 'add' : 'set';\n\t\t\tif ( ! $this->_memcached->$method($lock_key, time(), 300))\n\t\t\t{\n\t\t\t\tlog_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id);\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\t$this->_lock_key = $lock_key;\n\t\t\tbreak;\n\t\t}\n\t\twhile (++$attempt < 30);\n\n\t\tif ($attempt === 30)\n\t\t{\n\t\t\tlog_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 30 attempts, aborting.');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$this->_lock = TRUE;\n\t\treturn TRUE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Release lock\n\t *\n\t * Releases a previously acquired lock\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _release_lock()\n\t{\n\t\tif (isset($this->_memcached, $this->_lock_key) && $this->_lock)\n\t\t{\n\t\t\tif ( ! $this->_memcached->delete($this->_lock_key) && $this->_memcached->getResultCode() !== Memcached::RES_NOTFOUND)\n\t\t\t{\n\t\t\t\tlog_message('error', 'Session: Error while trying to free lock for '.$this->_lock_key);\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\t$this->_lock_key = NULL;\n\t\t\t$this->_lock = FALSE;\n\t\t}\n\n\t\treturn TRUE;\n\t}\n}\n"
  },
  {
    "path": "system/libraries/Session/drivers/Session_redis_driver.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 3.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * CodeIgniter Session Redis Driver\n *\n * @package\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tSessions\n * @author\tAndrey Andreev\n * @link\thttps://codeigniter.com/userguide3/libraries/sessions.html\n */\nclass CI_Session_redis_driver extends CI_Session_driver implements CI_Session_driver_interface {\n\n\t/**\n\t * phpRedis instance\n\t *\n\t * @var\tRedis\n\t */\n\tprotected $_redis;\n\n\t/**\n\t * Key prefix\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_key_prefix = 'ci_session:';\n\n\t/**\n\t * Lock key\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_lock_key;\n\n\t/**\n\t * Key exists flag\n\t *\n\t * @var bool\n\t */\n\tprotected $_key_exists = FALSE;\n\n\t/**\n\t * Name of setTimeout() method in phpRedis\n\t *\n\t * Due to some deprecated methods in phpRedis, we need to call the\n\t * specific methods depending on the version of phpRedis.\n\t *\n\t * @var string\n\t */\n\tprotected $_setTimeout_name;\n\n\t/**\n\t * Name of delete() method in phpRedis\n\t *\n\t * Due to some deprecated methods in phpRedis, we need to call the\n\t * specific methods depending on the version of phpRedis.\n\t *\n\t * @var string\n\t */\n\tprotected $_delete_name;\n\n\t/**\n\t * Success return value of ping() method in phpRedis\n\t *\n\t * @var mixed\n\t */\n\tprotected $_ping_success;\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Class constructor\n\t *\n\t * @param\tarray\t$params\tConfiguration parameters\n\t * @return\tvoid\n\t */\n\tpublic function __construct(&$params)\n\t{\n\t\tparent::__construct($params);\n\n\t\t// Detect the names of some methods in phpRedis instance\n\t\tif (version_compare(phpversion('redis'), '5', '>='))\n\t\t{\n\t\t\t$this->_setTimeout_name = 'expire';\n\t\t\t$this->_delete_name = 'del';\n\t\t\t$this->_ping_success = TRUE;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$this->_setTimeout_name = 'setTimeout';\n\t\t\t$this->_delete_name = 'delete';\n\t\t\t$this->_ping_success = '+PONG';\n\t\t}\n\n\t\tif (empty($this->_config['save_path']))\n\t\t{\n\t\t\tlog_message('error', 'Session: No Redis save path configured.');\n\t\t}\n\t\telseif (preg_match('#^unix://([^\\?]+)(?<options>\\?.+)?$#', $this->_config['save_path'], $matches))\n\t\t{\n\t\t\t$save_path = array('path' => $matches[1]);\n\t\t}\n\t\telseif (preg_match('#(?:(?:tcp|tls)://)?([^:?]+)(?:\\:(\\d+))?(?<options>\\?.+)?#', $this->_config['save_path'], $matches))\n\t\t{\n\t\t\t$save_path = array(\n\t\t\t\t'host'    => $matches[1],\n\t\t\t\t'port'    => empty($matches[2]) ? NULL : $matches[2],\n\t\t\t\t'timeout' => 0.0 // We always pass this to Redis::connect(), so it needs to exist\n\t\t\t);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlog_message('error', 'Session: Invalid Redis save path format: '.$this->_config['save_path']);\n\t\t}\n\n\t\tif (isset($save_path))\n\t\t{\n\t\t\tif (isset($matches['options']))\n\t\t\t{\n\t\t\t\t$save_path['password'] = preg_match('#auth=([^\\s&]+)#', $matches['options'], $match) ? $match[1] : NULL;\n\t\t\t\t$save_path['database'] = preg_match('#database=(\\d+)#', $matches['options'], $match) ? (int) $match[1] : NULL;\n\t\t\t\t$save_path['timeout']  = preg_match('#timeout=(\\d+\\.\\d+)#', $matches['options'], $match) ? (float) $match[1] : 0.0;\n\n\t\t\t\tpreg_match('#prefix=([^\\s&]+)#', $matches['options'], $match) && $this->_key_prefix = $match[1];\n\t\t\t}\n\n\t\t\t$this->_config['save_path'] = $save_path;\n\n\t\t\tif ($this->_config['match_ip'] === TRUE)\n\t\t\t{\n\t\t\t\t$this->_key_prefix .= $_SERVER['REMOTE_ADDR'].':';\n\t\t\t}\n\t\t}\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Open\n\t *\n\t * Sanitizes save_path and initializes connection.\n\t *\n\t * @param\tstring\t$save_path\tServer path\n\t * @param\tstring\t$name\t\tSession cookie name, unused\n\t * @return\tbool\n\t */\n\tpublic function open($save_path, $name)\n\t{\n\t\tif (empty($this->_config['save_path']))\n\t\t{\n\t\t\treturn $this->_failure;\n\t\t}\n\n\t\t$redis = new Redis();\n\t\t$connected = isset($this->_config['save_path']['path'])\n\t\t\t? $redis->connect($this->_config['save_path']['path'])\n\t\t\t: $redis->connect(\n\t\t\t\t$this->_config['save_path']['host'],\n\t\t\t\t$this->_config['save_path']['port'],\n\t\t\t\t$this->_config['save_path']['timeout']\n\t\t\t);\n\n\t\tif ($connected)\n\t\t{\n\t\t\tif (isset($this->_config['save_path']['password']) && ! $redis->auth($this->_config['save_path']['password']))\n\t\t\t{\n\t\t\t\tlog_message('error', 'Session: Unable to authenticate to Redis instance.');\n\t\t\t}\n\t\t\telseif (isset($this->_config['save_path']['database']) && ! $redis->select($this->_config['save_path']['database']))\n\t\t\t{\n\t\t\t\tlog_message('error', 'Session: Unable to select Redis database with index '.$this->_config['save_path']['database']);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$this->_redis = $redis;\n\t\t\t\t$this->php5_validate_id();\n\t\t\t\treturn $this->_success;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$this->_redis = $redis;\n\t\t\t$this->php5_validate_id();\n\t\t\treturn $this->_success;\n\t\t}\n\n\t\treturn $this->_failure;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Read\n\t *\n\t * Reads session data and acquires a lock\n\t *\n\t * @param\tstring\t$session_id\tSession ID\n\t * @return\tstring\tSerialized session data\n\t */\n\tpublic function read($session_id)\n\t{\n\t\tif (isset($this->_redis) && $this->_get_lock($session_id))\n\t\t{\n\t\t\t// Needed by write() to detect session_regenerate_id() calls\n\t\t\t$this->_session_id = $session_id;\n\n\t\t\t$session_data = $this->_redis->get($this->_key_prefix.$session_id);\n\n\t\t\tis_string($session_data)\n\t\t\t\t? $this->_key_exists = TRUE\n\t\t\t\t: $session_data = '';\n\n\t\t\t$this->_fingerprint = md5($session_data);\n\t\t\treturn $session_data;\n\t\t}\n\n\t\treturn $this->_failure;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Write\n\t *\n\t * Writes (create / update) session data\n\t *\n\t * @param\tstring\t$session_id\tSession ID\n\t * @param\tstring\t$session_data\tSerialized session data\n\t * @return\tbool\n\t */\n\tpublic function write($session_id, $session_data)\n\t{\n\t\tif ( ! isset($this->_redis, $this->_lock_key))\n\t\t{\n\t\t\treturn $this->_failure;\n\t\t}\n\t\t// Was the ID regenerated?\n\t\telseif ($session_id !== $this->_session_id)\n\t\t{\n\t\t\tif ( ! $this->_release_lock() OR ! $this->_get_lock($session_id))\n\t\t\t{\n\t\t\t\treturn $this->_failure;\n\t\t\t}\n\n\t\t\t$this->_key_exists = FALSE;\n\t\t\t$this->_session_id = $session_id;\n\t\t}\n\n\t\t$this->_redis->{$this->_setTimeout_name}($this->_lock_key, 300);\n\t\tif ($this->_fingerprint !== ($fingerprint = md5($session_data)) OR $this->_key_exists === FALSE)\n\t\t{\n\t\t\tif ($this->_redis->set($this->_key_prefix.$session_id, $session_data, $this->_config['expiration']))\n\t\t\t{\n\t\t\t\t$this->_fingerprint = $fingerprint;\n\t\t\t\t$this->_key_exists = TRUE;\n\t\t\t\treturn $this->_success;\n\t\t\t}\n\n\t\t\treturn $this->_failure;\n\t\t}\n\n\t\treturn ($this->_redis->{$this->_setTimeout_name}($this->_key_prefix.$session_id, $this->_config['expiration']))\n\t\t\t? $this->_success\n\t\t\t: $this->_failure;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Close\n\t *\n\t * Releases locks and closes connection.\n\t *\n\t * @return\tbool\n\t */\n\tpublic function close()\n\t{\n\t\tif (isset($this->_redis))\n\t\t{\n\t\t\ttry {\n\t\t\t\tif ($this->_redis->ping() === $this->_ping_success)\n\t\t\t\t{\n\t\t\t\t\t$this->_release_lock();\n\t\t\t\t\tif ($this->_redis->close() === FALSE)\n\t\t\t\t\t{\n\t\t\t\t\t\treturn $this->_failure;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (RedisException $e)\n\t\t\t{\n\t\t\t\tlog_message('error', 'Session: Got RedisException on close(): '.$e->getMessage());\n\t\t\t}\n\n\t\t\t$this->_redis = NULL;\n\t\t\treturn $this->_success;\n\t\t}\n\n\t\treturn $this->_success;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Destroy\n\t *\n\t * Destroys the current session.\n\t *\n\t * @param\tstring\t$session_id\tSession ID\n\t * @return\tbool\n\t */\n\tpublic function destroy($session_id)\n\t{\n\t\tif (isset($this->_redis, $this->_lock_key))\n\t\t{\n\t\t\tif (($result = $this->_redis->{$this->_delete_name}($this->_key_prefix.$session_id)) !== 1)\n\t\t\t{\n\t\t\t\tlog_message('debug', 'Session: Redis::'.$this->_delete_name.'() expected to return 1, got '.var_export($result, TRUE).' instead.');\n\t\t\t}\n\n\t\t\t$this->_cookie_destroy();\n\t\t\treturn $this->_success;\n\t\t}\n\n\t\treturn $this->_failure;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Garbage Collector\n\t *\n\t * Deletes expired sessions\n\t *\n\t * @param\tint \t$maxlifetime\tMaximum lifetime of sessions\n\t * @return\tbool\n\t */\n\tpublic function gc($maxlifetime)\n\t{\n\t\t// Not necessary, Redis takes care of that.\n\t\treturn $this->_success;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Update Timestamp\n\t *\n\t * Update session timestamp without modifying data\n\t *\n\t * @param\tstring\t$id\tSession ID\n\t * @param\tstring\t$data\tUnknown & unused\n\t * @return\tbool\n\t */\n\tpublic function updateTimestamp($id, $unknown)\n\t{\n\t\treturn $this->_redis->{$this->_setTimeout_name}($this->_key_prefix.$id, $this->_config['expiration']);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Validate ID\n\t *\n\t * Checks whether a session ID record exists server-side,\n\t * to enforce session.use_strict_mode.\n\t *\n\t * @param\tstring\t$id\tSession ID\n\t * @return\tbool\n\t */\n\tpublic function validateId($id)\n\t{\n\t\treturn (bool) $this->_redis->exists($this->_key_prefix.$id);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get lock\n\t *\n\t * Acquires an (emulated) lock.\n\t *\n\t * @param\tstring\t$session_id\tSession ID\n\t * @return\tbool\n\t */\n\tprotected function _get_lock($session_id)\n\t{\n\t\t// PHP 7 reuses the SessionHandler object on regeneration,\n\t\t// so we need to check here if the lock key is for the\n\t\t// correct session ID.\n\t\tif ($this->_lock_key === $this->_key_prefix.$session_id.':lock')\n\t\t{\n\t\t\treturn $this->_redis->{$this->_setTimeout_name}($this->_lock_key, 300);\n\t\t}\n\n\t\t// 30 attempts to obtain a lock, in case another request already has it\n\t\t$lock_key = $this->_key_prefix.$session_id.':lock';\n\t\t$attempt = 0;\n\t\tdo\n\t\t{\n\t\t\tif (($ttl = $this->_redis->ttl($lock_key)) > 0)\n\t\t\t{\n\t\t\t\tsleep(1);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ($ttl === -2 && ! $this->_redis->set($lock_key, time(), array('nx', 'ex' => 300)))\n\t\t\t{\n\t\t\t\t// Sleep for 1s to wait for lock releases.\n\t\t\t\tsleep(1);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telseif ( ! $this->_redis->setex($lock_key, 300, time()))\n\t\t\t{\n\t\t\t\tlog_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id);\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\t$this->_lock_key = $lock_key;\n\t\t\tbreak;\n\t\t}\n\t\twhile (++$attempt < 30);\n\n\t\tif ($attempt === 30)\n\t\t{\n\t\t\tlog_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 30 attempts, aborting.');\n\t\t\treturn FALSE;\n\t\t}\n\t\telseif ($ttl === -1)\n\t\t{\n\t\t\tlog_message('debug', 'Session: Lock for '.$this->_key_prefix.$session_id.' had no TTL, overriding.');\n\t\t}\n\n\t\t$this->_lock = TRUE;\n\t\treturn TRUE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Release lock\n\t *\n\t * Releases a previously acquired lock\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _release_lock()\n\t{\n\t\tif (isset($this->_redis, $this->_lock_key) && $this->_lock)\n\t\t{\n\t\t\tif ( ! $this->_redis->{$this->_delete_name}($this->_lock_key))\n\t\t\t{\n\t\t\t\tlog_message('error', 'Session: Error while trying to free lock for '.$this->_lock_key);\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\t$this->_lock_key = NULL;\n\t\t\t$this->_lock = FALSE;\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n}\n"
  },
  {
    "path": "system/libraries/Session/drivers/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/libraries/Session/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "system/libraries/Table.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.3.1\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * HTML Table Generating Class\n *\n * Lets you create tables manually or from database result objects, or arrays.\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tHTML Tables\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/table.html\n */\nclass CI_Table {\n\n\t/**\n\t * Data for table rows\n\t *\n\t * @var array\n\t */\n\tpublic $rows\t\t= array();\n\n\t/**\n\t * Data for table heading\n\t *\n\t * @var array\n\t */\n\tpublic $heading\t\t= array();\n\n\t/**\n\t * Whether or not to automatically create the table header\n\t *\n\t * @var bool\n\t */\n\tpublic $auto_heading\t= TRUE;\n\n\t/**\n\t * Table caption\n\t *\n\t * @var string\n\t */\n\tpublic $caption\t\t= NULL;\n\n\t/**\n\t * Table layout template\n\t *\n\t * @var array\n\t */\n\tpublic $template\t= NULL;\n\n\t/**\n\t * Newline setting\n\t *\n\t * @var string\n\t */\n\tpublic $newline\t\t= \"\\n\";\n\n\t/**\n\t * Contents of empty cells\n\t *\n\t * @var string\n\t */\n\tpublic $empty_cells\t= '';\n\n\t/**\n\t * Callback for custom table layout\n\t *\n\t * @var function\n\t */\n\tpublic $function\t= NULL;\n\n\t/**\n\t * Set the template from the table config file if it exists\n\t *\n\t * @param\tarray\t$config\t(default: array())\n\t * @return\tvoid\n\t */\n\tpublic function __construct($config = array())\n\t{\n\t\t// initialize config\n\t\tforeach ($config as $key => $val)\n\t\t{\n\t\t\t$this->template[$key] = $val;\n\t\t}\n\n\t\tlog_message('info', 'Table Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set the template\n\t *\n\t * @param\tarray\t$template\n\t * @return\tbool\n\t */\n\tpublic function set_template($template)\n\t{\n\t\tif ( ! is_array($template))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$this->template = $template;\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set the table heading\n\t *\n\t * Can be passed as an array or discreet params\n\t *\n\t * @param\tmixed\n\t * @return\tCI_Table\n\t */\n\tpublic function set_heading($args = array())\n\t{\n\t\t$this->heading = $this->_prep_args(func_get_args());\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set columns. Takes a one-dimensional array as input and creates\n\t * a multi-dimensional array with a depth equal to the number of\n\t * columns. This allows a single array with many elements to be\n\t * displayed in a table that has a fixed column count.\n\t *\n\t * @param\tarray\t$array\n\t * @param\tint\t$col_limit\n\t * @return\tarray\n\t */\n\tpublic function make_columns($array = array(), $col_limit = 0)\n\t{\n\t\tif ( ! is_array($array) OR count($array) === 0 OR ! is_int($col_limit))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Turn off the auto-heading feature since it's doubtful we\n\t\t// will want headings from a one-dimensional array\n\t\t$this->auto_heading = FALSE;\n\n\t\tif ($col_limit === 0)\n\t\t{\n\t\t\treturn $array;\n\t\t}\n\n\t\t$new = array();\n\t\tdo\n\t\t{\n\t\t\t$temp = array_splice($array, 0, $col_limit);\n\n\t\t\tif (count($temp) < $col_limit)\n\t\t\t{\n\t\t\t\tfor ($i = count($temp); $i < $col_limit; $i++)\n\t\t\t\t{\n\t\t\t\t\t$temp[] = '&nbsp;';\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$new[] = $temp;\n\t\t}\n\t\twhile (count($array) > 0);\n\n\t\treturn $new;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set \"empty\" cells\n\t *\n\t * Can be passed as an array or discreet params\n\t *\n\t * @param\tmixed\t$value\n\t * @return\tCI_Table\n\t */\n\tpublic function set_empty($value)\n\t{\n\t\t$this->empty_cells = $value;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Add a table row\n\t *\n\t * Can be passed as an array or discreet params\n\t *\n\t * @param\tmixed\n\t * @return\tCI_Table\n\t */\n\tpublic function add_row($args = array())\n\t{\n\t\t$this->rows[] = $this->_prep_args(func_get_args());\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Prep Args\n\t *\n\t * Ensures a standard associative array format for all cell data\n\t *\n\t * @param\tarray\n\t * @return\tarray\n\t */\n\tprotected function _prep_args($args)\n\t{\n\t\t// If there is no $args[0], skip this and treat as an associative array\n\t\t// This can happen if there is only a single key, for example this is passed to table->generate\n\t\t// array(array('foo'=>'bar'))\n\t\tif (isset($args[0]) && count($args) === 1 && is_array($args[0]) && ! isset($args[0]['data']))\n\t\t{\n\t\t\t$args = $args[0];\n\t\t}\n\n\t\tforeach ($args as $key => $val)\n\t\t{\n\t\t\tis_array($val) OR $args[$key] = array('data' => $val);\n\t\t}\n\n\t\treturn $args;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Add a table caption\n\t *\n\t * @param\tstring\t$caption\n\t * @return\tCI_Table\n\t */\n\tpublic function set_caption($caption)\n\t{\n\t\t$this->caption = $caption;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Generate the table\n\t *\n\t * @param\tmixed\t$table_data\n\t * @return\tstring\n\t */\n\tpublic function generate($table_data = NULL)\n\t{\n\t\t// The table data can optionally be passed to this function\n\t\t// either as a database result object or an array\n\t\tif ( ! empty($table_data))\n\t\t{\n\t\t\tif ($table_data instanceof CI_DB_result)\n\t\t\t{\n\t\t\t\t$this->_set_from_db_result($table_data);\n\t\t\t}\n\t\t\telseif (is_array($table_data))\n\t\t\t{\n\t\t\t\t$this->_set_from_array($table_data);\n\t\t\t}\n\t\t}\n\n\t\t// Is there anything to display? No? Smite them!\n\t\tif (empty($this->heading) && empty($this->rows))\n\t\t{\n\t\t\treturn 'Undefined table data';\n\t\t}\n\n\t\t// Compile and validate the template date\n\t\t$this->_compile_template();\n\n\t\t// Validate a possibly existing custom cell manipulation function\n\t\tif (isset($this->function) && ! is_callable($this->function))\n\t\t{\n\t\t\t$this->function = NULL;\n\t\t}\n\n\t\t// Build the table!\n\n\t\t$out = $this->template['table_open'].$this->newline;\n\n\t\t// Add any caption here\n\t\tif ($this->caption)\n\t\t{\n\t\t\t$out .= '<caption>'.$this->caption.'</caption>'.$this->newline;\n\t\t}\n\n\t\t// Is there a table heading to display?\n\t\tif ( ! empty($this->heading))\n\t\t{\n\t\t\t$out .= $this->template['thead_open'].$this->newline.$this->template['heading_row_start'].$this->newline;\n\n\t\t\tforeach ($this->heading as $heading)\n\t\t\t{\n\t\t\t\t$temp = $this->template['heading_cell_start'];\n\n\t\t\t\tforeach ($heading as $key => $val)\n\t\t\t\t{\n\t\t\t\t\tif ($key !== 'data')\n\t\t\t\t\t{\n\t\t\t\t\t\t$temp = str_replace('<th', '<th '.$key.'=\"'.$val.'\"', $temp);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$out .= $temp.(isset($heading['data']) ? $heading['data'] : '').$this->template['heading_cell_end'];\n\t\t\t}\n\n\t\t\t$out .= $this->template['heading_row_end'].$this->newline.$this->template['thead_close'].$this->newline;\n\t\t}\n\n\t\t// Build the table rows\n\t\tif ( ! empty($this->rows))\n\t\t{\n\t\t\t$out .= $this->template['tbody_open'].$this->newline;\n\n\t\t\t$i = 1;\n\t\t\tforeach ($this->rows as $row)\n\t\t\t{\n\t\t\t\tif ( ! is_array($row))\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t// We use modulus to alternate the row colors\n\t\t\t\t$name = fmod($i++, 2) ? '' : 'alt_';\n\n\t\t\t\t$out .= $this->template['row_'.$name.'start'].$this->newline;\n\n\t\t\t\tforeach ($row as $cell)\n\t\t\t\t{\n\t\t\t\t\t$temp = $this->template['cell_'.$name.'start'];\n\n\t\t\t\t\tforeach ($cell as $key => $val)\n\t\t\t\t\t{\n\t\t\t\t\t\tif ($key !== 'data')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t$temp = str_replace('<td', '<td '.$key.'=\"'.$val.'\"', $temp);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t$cell = isset($cell['data']) ? $cell['data'] : '';\n\t\t\t\t\t$out .= $temp;\n\n\t\t\t\t\tif ($cell === '' OR $cell === NULL)\n\t\t\t\t\t{\n\t\t\t\t\t\t$out .= $this->empty_cells;\n\t\t\t\t\t}\n\t\t\t\t\telseif (isset($this->function))\n\t\t\t\t\t{\n\t\t\t\t\t\t$out .= call_user_func($this->function, $cell);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t$out .= $cell;\n\t\t\t\t\t}\n\n\t\t\t\t\t$out .= $this->template['cell_'.$name.'end'];\n\t\t\t\t}\n\n\t\t\t\t$out .= $this->template['row_'.$name.'end'].$this->newline;\n\t\t\t}\n\n\t\t\t$out .= $this->template['tbody_close'].$this->newline;\n\t\t}\n\n\t\t$out .= $this->template['table_close'];\n\n\t\t// Clear table class properties before generating the table\n\t\t$this->clear();\n\n\t\treturn $out;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Clears the table arrays.  Useful if multiple tables are being generated\n\t *\n\t * @return\tCI_Table\n\t */\n\tpublic function clear()\n\t{\n\t\t$this->rows = array();\n\t\t$this->heading = array();\n\t\t$this->auto_heading = TRUE;\n\t\t$this->caption = NULL;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set table data from a database result object\n\t *\n\t * @param\tCI_DB_result\t$object\tDatabase result object\n\t * @return\tvoid\n\t */\n\tprotected function _set_from_db_result($object)\n\t{\n\t\t// First generate the headings from the table column names\n\t\tif ($this->auto_heading === TRUE && empty($this->heading))\n\t\t{\n\t\t\t$this->heading = $this->_prep_args($object->list_fields());\n\t\t}\n\n\t\tforeach ($object->result_array() as $row)\n\t\t{\n\t\t\t$this->rows[] = $this->_prep_args($row);\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set table data from an array\n\t *\n\t * @param\tarray\t$data\n\t * @return\tvoid\n\t */\n\tprotected function _set_from_array($data)\n\t{\n\t\tif ($this->auto_heading === TRUE && empty($this->heading))\n\t\t{\n\t\t\t$this->heading = $this->_prep_args(array_shift($data));\n\t\t}\n\n\t\tforeach ($data as &$row)\n\t\t{\n\t\t\t$this->rows[] = $this->_prep_args($row);\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Compile Template\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _compile_template()\n\t{\n\t\tif ($this->template === NULL)\n\t\t{\n\t\t\t$this->template = $this->_default_template();\n\t\t\treturn;\n\t\t}\n\n\t\t$this->temp = $this->_default_template();\n\t\tforeach (array('table_open', 'thead_open', 'thead_close', 'heading_row_start', 'heading_row_end', 'heading_cell_start', 'heading_cell_end', 'tbody_open', 'tbody_close', 'row_start', 'row_end', 'cell_start', 'cell_end', 'row_alt_start', 'row_alt_end', 'cell_alt_start', 'cell_alt_end', 'table_close') as $val)\n\t\t{\n\t\t\tif ( ! isset($this->template[$val]))\n\t\t\t{\n\t\t\t\t$this->template[$val] = $this->temp[$val];\n\t\t\t}\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Default Template\n\t *\n\t * @return\tarray\n\t */\n\tprotected function _default_template()\n\t{\n\t\treturn array(\n\t\t\t'table_open'\t\t=> '<table border=\"0\" cellpadding=\"4\" cellspacing=\"0\">',\n\n\t\t\t'thead_open'\t\t=> '<thead>',\n\t\t\t'thead_close'\t\t=> '</thead>',\n\n\t\t\t'heading_row_start'\t=> '<tr>',\n\t\t\t'heading_row_end'\t=> '</tr>',\n\t\t\t'heading_cell_start'\t=> '<th>',\n\t\t\t'heading_cell_end'\t=> '</th>',\n\n\t\t\t'tbody_open'\t\t=> '<tbody>',\n\t\t\t'tbody_close'\t\t=> '</tbody>',\n\n\t\t\t'row_start'\t\t=> '<tr>',\n\t\t\t'row_end'\t\t=> '</tr>',\n\t\t\t'cell_start'\t\t=> '<td>',\n\t\t\t'cell_end'\t\t=> '</td>',\n\n\t\t\t'row_alt_start'\t\t=> '<tr>',\n\t\t\t'row_alt_end'\t\t=> '</tr>',\n\t\t\t'cell_alt_start'\t=> '<td>',\n\t\t\t'cell_alt_end'\t\t=> '</td>',\n\n\t\t\t'table_close'\t\t=> '</table>'\n\t\t);\n\t}\n\n}\n"
  },
  {
    "path": "system/libraries/Trackback.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Trackback Class\n *\n * Trackback Sending/Receiving Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tTrackbacks\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/trackback.html\n */\nclass CI_Trackback {\n\n\t/**\n\t * Character set\n\t *\n\t * @var\tstring\n\t */\n\tpublic $charset = 'UTF-8';\n\n\t/**\n\t * Trackback data\n\t *\n\t * @var\tarray\n\t */\n\tpublic $data = array(\n\t\t'url' => '',\n\t\t'title' => '',\n\t\t'excerpt' => '',\n\t\t'blog_name' => '',\n\t\t'charset' => ''\n\t);\n\n\t/**\n\t * Convert ASCII flag\n\t *\n\t * Whether to convert high-ASCII and MS Word\n\t * characters to HTML entities.\n\t *\n\t * @var\tbool\n\t */\n\tpublic $convert_ascii = TRUE;\n\n\t/**\n\t * Response\n\t *\n\t * @var\tstring\n\t */\n\tpublic $response = '';\n\n\t/**\n\t * Error messages list\n\t *\n\t * @var\tstring[]\n\t */\n\tpublic $error_msg = array();\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Constructor\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __construct()\n\t{\n\t\tlog_message('info', 'Trackback Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Send Trackback\n\t *\n\t * @param\tarray\n\t * @return\tbool\n\t */\n\tpublic function send($tb_data)\n\t{\n\t\tif ( ! is_array($tb_data))\n\t\t{\n\t\t\t$this->set_error('The send() method must be passed an array');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Pre-process the Trackback Data\n\t\tforeach (array('url', 'title', 'excerpt', 'blog_name', 'ping_url') as $item)\n\t\t{\n\t\t\tif ( ! isset($tb_data[$item]))\n\t\t\t{\n\t\t\t\t$this->set_error('Required item missing: '.$item);\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\tswitch ($item)\n\t\t\t{\n\t\t\t\tcase 'ping_url':\n\t\t\t\t\t$$item = $this->extract_urls($tb_data[$item]);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'excerpt':\n\t\t\t\t\t$$item = $this->limit_characters($this->convert_xml(strip_tags(stripslashes($tb_data[$item]))));\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'url':\n\t\t\t\t\t$$item = str_replace('&#45;', '-', $this->convert_xml(strip_tags(stripslashes($tb_data[$item]))));\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\t$$item = $this->convert_xml(strip_tags(stripslashes($tb_data[$item])));\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Convert High ASCII Characters\n\t\t\tif ($this->convert_ascii === TRUE && in_array($item, array('excerpt', 'title', 'blog_name'), TRUE))\n\t\t\t{\n\t\t\t\t$$item = $this->convert_ascii($$item);\n\t\t\t}\n\t\t}\n\n\t\t// Build the Trackback data string\n\t\t$charset = isset($tb_data['charset']) ? $tb_data['charset'] : $this->charset;\n\n\t\t$data = 'url='.rawurlencode($url).'&title='.rawurlencode($title).'&blog_name='.rawurlencode($blog_name)\n\t\t\t.'&excerpt='.rawurlencode($excerpt).'&charset='.rawurlencode($charset);\n\n\t\t// Send Trackback(s)\n\t\t$return = TRUE;\n\t\tif (count($ping_url) > 0)\n\t\t{\n\t\t\tforeach ($ping_url as $url)\n\t\t\t{\n\t\t\t\tif ($this->process($url, $data) === FALSE)\n\t\t\t\t{\n\t\t\t\t\t$return = FALSE;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $return;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Receive Trackback  Data\n\t *\n\t * This function simply validates the incoming TB data.\n\t * It returns FALSE on failure and TRUE on success.\n\t * If the data is valid it is set to the $this->data array\n\t * so that it can be inserted into a database.\n\t *\n\t * @return\tbool\n\t */\n\tpublic function receive()\n\t{\n\t\tforeach (array('url', 'title', 'blog_name', 'excerpt') as $val)\n\t\t{\n\t\t\tif (empty($_POST[$val]))\n\t\t\t{\n\t\t\t\t$this->set_error('The following required POST variable is missing: '.$val);\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\t$this->data['charset'] = isset($_POST['charset']) ? strtoupper(trim($_POST['charset'])) : 'auto';\n\n\t\t\tif ($val !== 'url' && MB_ENABLED === TRUE)\n\t\t\t{\n\t\t\t\tif (MB_ENABLED === TRUE)\n\t\t\t\t{\n\t\t\t\t\t$_POST[$val] = mb_convert_encoding($_POST[$val], $this->charset, $this->data['charset']);\n\t\t\t\t}\n\t\t\t\telseif (ICONV_ENABLED === TRUE)\n\t\t\t\t{\n\t\t\t\t\t$_POST[$val] = @iconv($this->data['charset'], $this->charset.'//IGNORE', $_POST[$val]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$_POST[$val] = ($val !== 'url') ? $this->convert_xml(strip_tags($_POST[$val])) : strip_tags($_POST[$val]);\n\n\t\t\tif ($val === 'excerpt')\n\t\t\t{\n\t\t\t\t$_POST['excerpt'] = $this->limit_characters($_POST['excerpt']);\n\t\t\t}\n\n\t\t\t$this->data[$val] = $_POST[$val];\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Send Trackback Error Message\n\t *\n\t * Allows custom errors to be set. By default it\n\t * sends the \"incomplete information\" error, as that's\n\t * the most common one.\n\t *\n\t * @param\tstring\n\t * @return\tvoid\n\t */\n\tpublic function send_error($message = 'Incomplete Information')\n\t{\n\t\texit('<?xml version=\"1.0\" encoding=\"utf-8\"?'.\">\\n<response>\\n<error>1</error>\\n<message>\".$message.\"</message>\\n</response>\");\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Send Trackback Success Message\n\t *\n\t * This should be called when a trackback has been\n\t * successfully received and inserted.\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function send_success()\n\t{\n\t\texit('<?xml version=\"1.0\" encoding=\"utf-8\"?'.\">\\n<response>\\n<error>0</error>\\n</response>\");\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fetch a particular item\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tpublic function data($item)\n\t{\n\t\treturn isset($this->data[$item]) ? $this->data[$item] : '';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Process Trackback\n\t *\n\t * Opens a socket connection and passes the data to\n\t * the server. Returns TRUE on success, FALSE on failure\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tbool\n\t */\n\tpublic function process($url, $data)\n\t{\n\t\t$target = parse_url($url);\n\n\t\t// Open the socket\n\t\tif ( ! $fp = @fsockopen($target['host'], 80))\n\t\t{\n\t\t\t$this->set_error('Invalid Connection: '.$url);\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Build the path\n\t\t$path = isset($target['path']) ? $target['path'] : $url;\n\t\tempty($target['query']) OR $path .= '?'.$target['query'];\n\n\t\t// Add the Trackback ID to the data string\n\t\tif ($id = $this->get_id($url))\n\t\t{\n\t\t\t$data = 'tb_id='.$id.'&'.$data;\n\t\t}\n\n\t\t// Transfer the data\n\t\tfputs($fp, 'POST '.$path.\" HTTP/1.0\\r\\n\");\n\t\tfputs($fp, 'Host: '.$target['host'].\"\\r\\n\");\n\t\tfputs($fp, \"Content-type: application/x-www-form-urlencoded\\r\\n\");\n\t\tfputs($fp, 'Content-length: '.strlen($data).\"\\r\\n\");\n\t\tfputs($fp, \"Connection: close\\r\\n\\r\\n\");\n\t\tfputs($fp, $data);\n\n\t\t// Was it successful?\n\n\t\t$this->response = '';\n\t\twhile ( ! feof($fp))\n\t\t{\n\t\t\t$this->response .= fgets($fp, 128);\n\t\t}\n\t\t@fclose($fp);\n\n\t\tif (stripos($this->response, '<error>0</error>') === FALSE)\n\t\t{\n\t\t\t$message = preg_match('/<message>(.*?)<\\/message>/is', $this->response, $match)\n\t\t\t\t? trim($match[1])\n\t\t\t\t: 'An unknown error was encountered';\n\t\t\t$this->set_error($message);\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Extract Trackback URLs\n\t *\n\t * This function lets multiple trackbacks be sent.\n\t * It takes a string of URLs (separated by comma or\n\t * space) and puts each URL into an array\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tpublic function extract_urls($urls)\n\t{\n\t\t// Remove the pesky white space and replace with a comma, then replace doubles.\n\t\t$urls = str_replace(',,', ',', preg_replace('/\\s*(\\S+)\\s*/', '\\\\1,', $urls));\n\n\t\t// Break into an array via commas and remove duplicates\n\t\t$urls = array_unique(preg_split('/[,]/', rtrim($urls, ',')));\n\n\t\tarray_walk($urls, array($this, 'validate_url'));\n\t\treturn $urls;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Validate URL\n\t *\n\t * Simply adds \"http://\" if missing\n\t *\n\t * @param\tstring\n\t * @return\tvoid\n\t */\n\tpublic function validate_url(&$url)\n\t{\n\t\t$url = trim($url);\n\n\t\tif (stripos($url, 'http') !== 0)\n\t\t{\n\t\t\t$url = 'http://'.$url;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Find the Trackback URL's ID\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tpublic function get_id($url)\n\t{\n\t\t$tb_id = '';\n\n\t\tif (strpos($url, '?') !== FALSE)\n\t\t{\n\t\t\t$tb_array = explode('/', $url);\n\t\t\t$tb_end   = $tb_array[count($tb_array)-1];\n\n\t\t\tif ( ! is_numeric($tb_end))\n\t\t\t{\n\t\t\t\t$tb_end  = $tb_array[count($tb_array)-2];\n\t\t\t}\n\n\t\t\t$tb_array = explode('=', $tb_end);\n\t\t\t$tb_id\t= $tb_array[count($tb_array)-1];\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$url = rtrim($url, '/');\n\n\t\t\t$tb_array = explode('/', $url);\n\t\t\t$tb_id\t= $tb_array[count($tb_array)-1];\n\n\t\t\tif ( ! is_numeric($tb_id))\n\t\t\t{\n\t\t\t\t$tb_id = $tb_array[count($tb_array)-2];\n\t\t\t}\n\t\t}\n\n\t\treturn ctype_digit((string) $tb_id) ? $tb_id : FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Convert Reserved XML characters to Entities\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tpublic function convert_xml($str)\n\t{\n\t\t$temp = '__TEMP_AMPERSANDS__';\n\n\t\t$str = preg_replace(array('/&#(\\d+);/', '/&(\\w+);/'), $temp.'\\\\1;', $str);\n\n\t\t$str = str_replace(array('&', '<', '>', '\"', \"'\", '-'),\n\t\t\t\t\tarray('&amp;', '&lt;', '&gt;', '&quot;', '&#39;', '&#45;'),\n\t\t\t\t\t$str);\n\n\t\treturn preg_replace(array('/'.$temp.'(\\d+);/', '/'.$temp.'(\\w+);/'), array('&#\\\\1;', '&\\\\1;'), $str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Character limiter\n\t *\n\t * Limits the string based on the character count. Will preserve complete words.\n\t *\n\t * @param\tstring\n\t * @param\tint\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tpublic function limit_characters($str, $n = 500, $end_char = '&#8230;')\n\t{\n\t\tif (strlen($str) < $n)\n\t\t{\n\t\t\treturn $str;\n\t\t}\n\n\t\t$str = preg_replace('/\\s+/', ' ', str_replace(array(\"\\r\\n\", \"\\r\", \"\\n\"), ' ', $str));\n\n\t\tif (strlen($str) <= $n)\n\t\t{\n\t\t\treturn $str;\n\t\t}\n\n\t\t$out = '';\n\t\tforeach (explode(' ', trim($str)) as $val)\n\t\t{\n\t\t\t$out .= $val.' ';\n\t\t\tif (strlen($out) >= $n)\n\t\t\t{\n\t\t\t\treturn rtrim($out).$end_char;\n\t\t\t}\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * High ASCII to Entities\n\t *\n\t * Converts Hight ascii text and MS Word special chars\n\t * to character entities\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tpublic function convert_ascii($str)\n\t{\n\t\t$count\t= 1;\n\t\t$out\t= '';\n\t\t$temp\t= array();\n\n\t\tfor ($i = 0, $s = strlen($str); $i < $s; $i++)\n\t\t{\n\t\t\t$ordinal = ord($str[$i]);\n\n\t\t\tif ($ordinal < 128)\n\t\t\t{\n\t\t\t\t$out .= $str[$i];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (count($temp) === 0)\n\t\t\t\t{\n\t\t\t\t\t$count = ($ordinal < 224) ? 2 : 3;\n\t\t\t\t}\n\n\t\t\t\t$temp[] = $ordinal;\n\n\t\t\t\tif (count($temp) === $count)\n\t\t\t\t{\n\t\t\t\t\t$number = ($count === 3)\n\t\t\t\t\t\t? (($temp[0] % 16) * 4096) + (($temp[1] % 64) * 64) + ($temp[2] % 64)\n\t\t\t\t\t\t: (($temp[0] % 32) * 64) + ($temp[1] % 64);\n\n\t\t\t\t\t$out .= '&#'.$number.';';\n\t\t\t\t\t$count = 1;\n\t\t\t\t\t$temp = array();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $out;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set error message\n\t *\n\t * @param\tstring\n\t * @return\tvoid\n\t */\n\tpublic function set_error($msg)\n\t{\n\t\tlog_message('error', $msg);\n\t\t$this->error_msg[] = $msg;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Show error messages\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tpublic function display_errors($open = '<p>', $close = '</p>')\n\t{\n\t\treturn (count($this->error_msg) > 0) ? $open.implode($close.$open, $this->error_msg).$close : '';\n\t}\n\n}\n"
  },
  {
    "path": "system/libraries/Typography.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Typography Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tHelpers\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/typography.html\n */\nclass CI_Typography {\n\n\t/**\n\t * Block level elements that should not be wrapped inside <p> tags\n\t *\n\t * @var string\n\t */\n\tpublic $block_elements = 'address|blockquote|div|dl|fieldset|form|h\\d|hr|noscript|object|ol|p|pre|script|table|ul';\n\n\t/**\n\t * Elements that should not have <p> and <br /> tags within them.\n\t *\n\t * @var string\n\t */\n\tpublic $skip_elements\t= 'p|pre|ol|ul|dl|object|table|h\\d';\n\n\t/**\n\t * Tags we want the parser to completely ignore when splitting the string.\n\t *\n\t * @var string\n\t */\n\tpublic $inline_elements = 'a|abbr|acronym|b|bdo|big|br|button|cite|code|del|dfn|em|i|img|ins|input|label|map|kbd|q|samp|select|small|span|strong|sub|sup|textarea|tt|var';\n\n\t/**\n\t * array of block level elements that require inner content to be within another block level element\n\t *\n\t * @var array\n\t */\n\tpublic $inner_block_required = array('blockquote');\n\n\t/**\n\t * the last block element parsed\n\t *\n\t * @var string\n\t */\n\tpublic $last_block_element = '';\n\n\t/**\n\t * whether or not to protect quotes within { curly braces }\n\t *\n\t * @var bool\n\t */\n\tpublic $protect_braced_quotes = FALSE;\n\n\t/**\n\t * Auto Typography\n\t *\n\t * This function converts text, making it typographically correct:\n\t *\t- Converts double spaces into paragraphs.\n\t *\t- Converts single line breaks into <br /> tags\n\t *\t- Converts single and double quotes into correctly facing curly quote entities.\n\t *\t- Converts three dots into ellipsis.\n\t *\t- Converts double dashes into em-dashes.\n\t *  - Converts two spaces into entities\n\t *\n\t * @param\tstring\n\t * @param\tbool\twhether to reduce more then two consecutive newlines to two\n\t * @return\tstring\n\t */\n\tpublic function auto_typography($str, $reduce_linebreaks = FALSE)\n\t{\n\t\tif ($str === '')\n\t\t{\n\t\t\treturn '';\n\t\t}\n\n\t\t// Standardize Newlines to make matching easier\n\t\tif (strpos($str, \"\\r\") !== FALSE)\n\t\t{\n\t\t\t$str = str_replace(array(\"\\r\\n\", \"\\r\"), \"\\n\", $str);\n\t\t}\n\n\t\t// Reduce line breaks.  If there are more than two consecutive linebreaks\n\t\t// we'll compress them down to a maximum of two since there's no benefit to more.\n\t\tif ($reduce_linebreaks === TRUE)\n\t\t{\n\t\t\t$str = preg_replace(\"/\\n\\n+/\", \"\\n\\n\", $str);\n\t\t}\n\n\t\t// HTML comment tags don't conform to patterns of normal tags, so pull them out separately, only if needed\n\t\t$html_comments = array();\n\t\tif (strpos($str, '<!--') !== FALSE && preg_match_all('#(<!\\-\\-.*?\\-\\->)#s', $str, $matches))\n\t\t{\n\t\t\tfor ($i = 0, $total = count($matches[0]); $i < $total; $i++)\n\t\t\t{\n\t\t\t\t$html_comments[] = $matches[0][$i];\n\t\t\t\t$str = str_replace($matches[0][$i], '{@HC'.$i.'}', $str);\n\t\t\t}\n\t\t}\n\n\t\t// match and yank <pre> tags if they exist.  It's cheaper to do this separately since most content will\n\t\t// not contain <pre> tags, and it keeps the PCRE patterns below simpler and faster\n\t\tif (strpos($str, '<pre') !== FALSE)\n\t\t{\n\t\t\t$str = preg_replace_callback('#<pre.*?>.*?</pre>#si', array($this, '_protect_characters'), $str);\n\t\t}\n\n\t\t// Convert quotes within tags to temporary markers.\n\t\t$str = preg_replace_callback('#<.+?>#si', array($this, '_protect_characters'), $str);\n\n\t\t// Do the same with braces if necessary\n\t\tif ($this->protect_braced_quotes === TRUE)\n\t\t{\n\t\t\t$str = preg_replace_callback('#\\{.+?\\}#si', array($this, '_protect_characters'), $str);\n\t\t}\n\n\t\t// Convert \"ignore\" tags to temporary marker.  The parser splits out the string at every tag\n\t\t// it encounters.  Certain inline tags, like image tags, links, span tags, etc. will be\n\t\t// adversely affected if they are split out so we'll convert the opening bracket < temporarily to: {@TAG}\n\t\t$str = preg_replace('#<(/*)('.$this->inline_elements.')([ >])#i', '{@TAG}\\\\1\\\\2\\\\3', $str);\n\n\t\t/* Split the string at every tag. This expression creates an array with this prototype:\n\t\t *\n\t\t *\t[array]\n\t\t *\t{\n\t\t *\t\t[0] = <opening tag>\n\t\t *\t\t[1] = Content...\n\t\t *\t\t[2] = <closing tag>\n\t\t *\t\tEtc...\n\t\t *\t}\n\t\t */\n\t\t$chunks = preg_split('/(<(?:[^<>]+(?:\"[^\"]*\"|\\'[^\\']*\\')?)+>)/', $str, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);\n\n\t\t// Build our finalized string.  We cycle through the array, skipping tags, and processing the contained text\n\t\t$str = '';\n\t\t$process = TRUE;\n\n\t\tfor ($i = 0, $c = count($chunks) - 1; $i <= $c; $i++)\n\t\t{\n\t\t\t// Are we dealing with a tag? If so, we'll skip the processing for this cycle.\n\t\t\t// Well also set the \"process\" flag which allows us to skip <pre> tags and a few other things.\n\t\t\tif (preg_match('#<(/*)('.$this->block_elements.').*?>#', $chunks[$i], $match))\n\t\t\t{\n\t\t\t\tif (preg_match('#'.$this->skip_elements.'#', $match[2]))\n\t\t\t\t{\n\t\t\t\t\t$process = ($match[1] === '/');\n\t\t\t\t}\n\n\t\t\t\tif ($match[1] === '')\n\t\t\t\t{\n\t\t\t\t\t$this->last_block_element = $match[2];\n\t\t\t\t}\n\n\t\t\t\t$str .= $chunks[$i];\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ($process === FALSE)\n\t\t\t{\n\t\t\t\t$str .= $chunks[$i];\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t//  Force a newline to make sure end tags get processed by _format_newlines()\n\t\t\tif ($i === $c)\n\t\t\t{\n\t\t\t\t$chunks[$i] .= \"\\n\";\n\t\t\t}\n\n\t\t\t//  Convert Newlines into <p> and <br /> tags\n\t\t\t$str .= $this->_format_newlines($chunks[$i]);\n\t\t}\n\n\t\t// No opening block level tag? Add it if needed.\n\t\tif ( ! preg_match('/^\\s*<(?:'.$this->block_elements.')/i', $str))\n\t\t{\n\t\t\t$str = preg_replace('/^(.*?)<('.$this->block_elements.')/i', '<p>$1</p><$2', $str);\n\t\t}\n\n\t\t// Convert quotes, elipsis, em-dashes, non-breaking spaces, and ampersands\n\t\t$str = $this->format_characters($str);\n\n\t\t// restore HTML comments\n\t\tfor ($i = 0, $total = count($html_comments); $i < $total; $i++)\n\t\t{\n\t\t\t// remove surrounding paragraph tags, but only if there's an opening paragraph tag\n\t\t\t// otherwise HTML comments at the ends of paragraphs will have the closing tag removed\n\t\t\t// if '<p>{@HC1}' then replace <p>{@HC1}</p> with the comment, else replace only {@HC1} with the comment\n\t\t\t$str = preg_replace('#(?(?=<p>\\{@HC'.$i.'\\})<p>\\{@HC'.$i.'\\}(\\s*</p>)|\\{@HC'.$i.'\\})#s', $html_comments[$i], $str);\n\t\t}\n\n\t\t// Final clean up\n\t\t$table = array(\n\n\t\t\t\t\t\t// If the user submitted their own paragraph tags within the text\n\t\t\t\t\t\t// we will retain them instead of using our tags.\n\t\t\t\t\t\t'/(<p[^>*?]>)<p>/'\t=> '$1', // <?php BBEdit syntax coloring bug fix\n\n\t\t\t\t\t\t// Reduce multiple instances of opening/closing paragraph tags to a single one\n\t\t\t\t\t\t'#(</p>)+#'\t\t\t=> '</p>',\n\t\t\t\t\t\t'/(<p>\\W*<p>)+/'\t=> '<p>',\n\n\t\t\t\t\t\t// Clean up stray paragraph tags that appear before block level elements\n\t\t\t\t\t\t'#<p></p><('.$this->block_elements.')#'\t=> '<$1',\n\n\t\t\t\t\t\t// Clean up stray non-breaking spaces preceding block elements\n\t\t\t\t\t\t'#(&nbsp;\\s*)+<('.$this->block_elements.')#'\t=> '  <$2',\n\n\t\t\t\t\t\t// Replace the temporary markers we added earlier\n\t\t\t\t\t\t'/\\{@TAG\\}/'\t\t=> '<',\n\t\t\t\t\t\t'/\\{@DQ\\}/'\t\t\t=> '\"',\n\t\t\t\t\t\t'/\\{@SQ\\}/'\t\t\t=> \"'\",\n\t\t\t\t\t\t'/\\{@DD\\}/'\t\t\t=> '--',\n\t\t\t\t\t\t'/\\{@NBS\\}/'\t\t=> '  ',\n\n\t\t\t\t\t\t// An unintended consequence of the _format_newlines function is that\n\t\t\t\t\t\t// some of the newlines get truncated, resulting in <p> tags\n\t\t\t\t\t\t// starting immediately after <block> tags on the same line.\n\t\t\t\t\t\t// This forces a newline after such occurrences, which looks much nicer.\n\t\t\t\t\t\t\"/><p>\\n/\"\t\t\t=> \">\\n<p>\",\n\n\t\t\t\t\t\t// Similarly, there might be cases where a closing </block> will follow\n\t\t\t\t\t\t// a closing </p> tag, so we'll correct it by adding a newline in between\n\t\t\t\t\t\t'#</p></#'\t\t\t=> \"</p>\\n</\"\n\t\t\t\t\t\t);\n\n\t\t// Do we need to reduce empty lines?\n\t\tif ($reduce_linebreaks === TRUE)\n\t\t{\n\t\t\t$table['#<p>\\n*</p>#'] = '';\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// If we have empty paragraph tags we add a non-breaking space\n\t\t\t// otherwise most browsers won't treat them as true paragraphs\n\t\t\t$table['#<p></p>#'] = '<p>&nbsp;</p>';\n\t\t}\n\n\t\treturn preg_replace(array_keys($table), $table, $str);\n\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Format Characters\n\t *\n\t * This function mainly converts double and single quotes\n\t * to curly entities, but it also converts em-dashes,\n\t * double spaces, and ampersands\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tpublic function format_characters($str)\n\t{\n\t\tstatic $table;\n\n\t\tif ( ! isset($table))\n\t\t{\n\t\t\t$table = array(\n\t\t\t\t\t\t\t// nested smart quotes, opening and closing\n\t\t\t\t\t\t\t// note that rules for grammar (English) allow only for two levels deep\n\t\t\t\t\t\t\t// and that single quotes are _supposed_ to always be on the outside\n\t\t\t\t\t\t\t// but we'll accommodate both\n\t\t\t\t\t\t\t// Note that in all cases, whitespace is the primary determining factor\n\t\t\t\t\t\t\t// on which direction to curl, with non-word characters like punctuation\n\t\t\t\t\t\t\t// being a secondary factor only after whitespace is addressed.\n\t\t\t\t\t\t\t'/\\'\"(\\s|$)/'\t\t\t\t\t=> '&#8217;&#8221;$1',\n\t\t\t\t\t\t\t'/(^|\\s|<p>)\\'\"/'\t\t\t\t=> '$1&#8216;&#8220;',\n\t\t\t\t\t\t\t'/\\'\"(\\W)/'\t\t\t\t\t\t=> '&#8217;&#8221;$1',\n\t\t\t\t\t\t\t'/(\\W)\\'\"/'\t\t\t\t\t\t=> '$1&#8216;&#8220;',\n\t\t\t\t\t\t\t'/\"\\'(\\s|$)/'\t\t\t\t\t=> '&#8221;&#8217;$1',\n\t\t\t\t\t\t\t'/(^|\\s|<p>)\"\\'/'\t\t\t\t=> '$1&#8220;&#8216;',\n\t\t\t\t\t\t\t'/\"\\'(\\W)/'\t\t\t\t\t\t=> '&#8221;&#8217;$1',\n\t\t\t\t\t\t\t'/(\\W)\"\\'/'\t\t\t\t\t\t=> '$1&#8220;&#8216;',\n\n\t\t\t\t\t\t\t// single quote smart quotes\n\t\t\t\t\t\t\t'/\\'(\\s|$)/'\t\t\t\t\t=> '&#8217;$1',\n\t\t\t\t\t\t\t'/(^|\\s|<p>)\\'/'\t\t\t\t=> '$1&#8216;',\n\t\t\t\t\t\t\t'/\\'(\\W)/'\t\t\t\t\t\t=> '&#8217;$1',\n\t\t\t\t\t\t\t'/(\\W)\\'/'\t\t\t\t\t\t=> '$1&#8216;',\n\n\t\t\t\t\t\t\t// double quote smart quotes\n\t\t\t\t\t\t\t'/\"(\\s|$)/'\t\t\t\t\t\t=> '&#8221;$1',\n\t\t\t\t\t\t\t'/(^|\\s|<p>)\"/'\t\t\t\t\t=> '$1&#8220;',\n\t\t\t\t\t\t\t'/\"(\\W)/'\t\t\t\t\t\t=> '&#8221;$1',\n\t\t\t\t\t\t\t'/(\\W)\"/'\t\t\t\t\t\t=> '$1&#8220;',\n\n\t\t\t\t\t\t\t// apostrophes\n\t\t\t\t\t\t\t\"/(\\w)'(\\w)/\"\t\t\t\t\t=> '$1&#8217;$2',\n\n\t\t\t\t\t\t\t// Em dash and ellipses dots\n\t\t\t\t\t\t\t'/\\s?\\-\\-\\s?/'\t\t\t\t\t=> '&#8212;',\n\t\t\t\t\t\t\t'/(\\w)\\.{3}/'\t\t\t\t\t=> '$1&#8230;',\n\n\t\t\t\t\t\t\t// double space after sentences\n\t\t\t\t\t\t\t'/(\\W)  /'\t\t\t\t\t\t=> '$1&nbsp; ',\n\n\t\t\t\t\t\t\t// ampersands, if not a character entity\n\t\t\t\t\t\t\t'/&(?!#?[a-zA-Z0-9]{2,};)/'\t\t=> '&amp;'\n\t\t\t\t\t\t);\n\t\t}\n\n\t\treturn preg_replace(array_keys($table), $table, $str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Format Newlines\n\t *\n\t * Converts newline characters into either <p> tags or <br />\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tprotected function _format_newlines($str)\n\t{\n\t\tif ($str === '' OR (strpos($str, \"\\n\") === FALSE && ! in_array($this->last_block_element, $this->inner_block_required)))\n\t\t{\n\t\t\treturn $str;\n\t\t}\n\n\t\t// Convert two consecutive newlines to paragraphs\n\t\t$str = str_replace(\"\\n\\n\", \"</p>\\n\\n<p>\", $str);\n\n\t\t// Convert single spaces to <br /> tags\n\t\t$str = preg_replace(\"/([^\\n])(\\n)([^\\n])/\", '\\\\1<br />\\\\2\\\\3', $str);\n\n\t\t// Wrap the whole enchilada in enclosing paragraphs\n\t\tif ($str !== \"\\n\")\n\t\t{\n\t\t\t// We trim off the right-side new line so that the closing </p> tag\n\t\t\t// will be positioned immediately following the string, matching\n\t\t\t// the behavior of the opening <p> tag\n\t\t\t$str =  '<p>'.rtrim($str).'</p>';\n\t\t}\n\n\t\t// Remove empty paragraphs if they are on the first line, as this\n\t\t// is a potential unintended consequence of the previous code\n\t\treturn preg_replace('/<p><\\/p>(.*)/', '\\\\1', $str, 1);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Protect Characters\n\t *\n\t * Protects special characters from being formatted later\n\t * We don't want quotes converted within tags so we'll temporarily convert them to {@DQ} and {@SQ}\n\t * and we don't want double dashes converted to emdash entities, so they are marked with {@DD}\n\t * likewise double spaces are converted to {@NBS} to prevent entity conversion\n\t *\n\t * @param\tarray\n\t * @return\tstring\n\t */\n\tprotected function _protect_characters($match)\n\t{\n\t\treturn str_replace(array(\"'\",'\"','--','  '), array('{@SQ}', '{@DQ}', '{@DD}', '{@NBS}'), $match[0]);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Convert newlines to HTML line breaks except within PRE tags\n\t *\n\t * @param\tstring\n\t * @return\tstring\n\t */\n\tpublic function nl2br_except_pre($str)\n\t{\n\t\t$newstr = '';\n\t\tfor ($ex = explode('pre>', $str), $ct = count($ex), $i = 0; $i < $ct; $i++)\n\t\t{\n\t\t\t$newstr .= (($i % 2) === 0) ? nl2br($ex[$i]) : $ex[$i];\n\t\t\tif ($ct - 1 !== $i)\n\t\t\t{\n\t\t\t\t$newstr .= 'pre>';\n\t\t\t}\n\t\t}\n\n\t\treturn $newstr;\n\t}\n\n}\n"
  },
  {
    "path": "system/libraries/Unit_test.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.3.1\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Unit Testing Class\n *\n * Simple testing class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tUnitTesting\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/unit_testing.html\n */\nclass CI_Unit_test {\n\n\t/**\n\t * Active flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $active = TRUE;\n\n\t/**\n\t * Test results\n\t *\n\t * @var\tarray\n\t */\n\tpublic $results = array();\n\n\t/**\n\t * Strict comparison flag\n\t *\n\t * Whether to use === or == when comparing\n\t *\n\t * @var\tbool\n\t */\n\tpublic $strict = FALSE;\n\n\t/**\n\t * Template\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_template = NULL;\n\n\t/**\n\t * Template rows\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_template_rows = NULL;\n\n\t/**\n\t * List of visible test items\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_test_items_visible\t= array(\n\t\t'test_name',\n\t\t'test_datatype',\n\t\t'res_datatype',\n\t\t'result',\n\t\t'file',\n\t\t'line',\n\t\t'notes'\n\t);\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Constructor\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __construct()\n\t{\n\t\tlog_message('info', 'Unit Testing Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Run the tests\n\t *\n\t * Runs the supplied tests\n\t *\n\t * @param\tarray\t$items\n\t * @return\tvoid\n\t */\n\tpublic function set_test_items($items)\n\t{\n\t\tif ( ! empty($items) && is_array($items))\n\t\t{\n\t\t\t$this->_test_items_visible = $items;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Run the tests\n\t *\n\t * Runs the supplied tests\n\t *\n\t * @param\tmixed\t$test\n\t * @param\tmixed\t$expected\n\t * @param\tstring\t$test_name\n\t * @param\tstring\t$notes\n\t * @return\tstring\n\t */\n\tpublic function run($test, $expected = TRUE, $test_name = 'undefined', $notes = '')\n\t{\n\t\tif ($this->active === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif (in_array($expected, array('is_object', 'is_string', 'is_bool', 'is_true', 'is_false', 'is_int', 'is_numeric', 'is_float', 'is_double', 'is_array', 'is_null', 'is_resource'), TRUE))\n\t\t{\n\t\t\t$result = $expected($test);\n\t\t\t$extype = str_replace(array('true', 'false'), 'bool', str_replace('is_', '', $expected));\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$result = ($this->strict === TRUE) ? ($test === $expected) : ($test == $expected);\n\t\t\t$extype = gettype($expected);\n\t\t}\n\n\t\t$back = $this->_backtrace();\n\n\t\t$report = array (\n\t\t\t'test_name'     => $test_name,\n\t\t\t'test_datatype' => gettype($test),\n\t\t\t'res_datatype'  => $extype,\n\t\t\t'result'        => ($result === TRUE) ? 'passed' : 'failed',\n\t\t\t'file'          => $back['file'],\n\t\t\t'line'          => $back['line'],\n\t\t\t'notes'         => $notes\n\t\t);\n\n\t\t$this->results[] = $report;\n\n\t\treturn $this->report($this->result(array($report)));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Generate a report\n\t *\n\t * Displays a table with the test data\n\t *\n\t * @param\tarray\t $result\n\t * @return\tstring\n\t */\n\tpublic function report($result = array())\n\t{\n\t\tif (count($result) === 0)\n\t\t{\n\t\t\t$result = $this->result();\n\t\t}\n\n\t\t$CI =& get_instance();\n\t\t$CI->load->language('unit_test');\n\n\t\t$this->_parse_template();\n\n\t\t$r = '';\n\t\tforeach ($result as $res)\n\t\t{\n\t\t\t$table = '';\n\n\t\t\tforeach ($res as $key => $val)\n\t\t\t{\n\t\t\t\tif ($key === $CI->lang->line('ut_result'))\n\t\t\t\t{\n\t\t\t\t\tif ($val === $CI->lang->line('ut_passed'))\n\t\t\t\t\t{\n\t\t\t\t\t\t$val = '<span style=\"color: #0C0;\">'.$val.'</span>';\n\t\t\t\t\t}\n\t\t\t\t\telseif ($val === $CI->lang->line('ut_failed'))\n\t\t\t\t\t{\n\t\t\t\t\t\t$val = '<span style=\"color: #C00;\">'.$val.'</span>';\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$table .= str_replace(array('{item}', '{result}'), array($key, $val), $this->_template_rows);\n\t\t\t}\n\n\t\t\t$r .= str_replace('{rows}', $table, $this->_template);\n\t\t}\n\n\t\treturn $r;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Use strict comparison\n\t *\n\t * Causes the evaluation to use === rather than ==\n\t *\n\t * @param\tbool\t$state\n\t * @return\tvoid\n\t */\n\tpublic function use_strict($state = TRUE)\n\t{\n\t\t$this->strict = (bool) $state;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Make Unit testing active\n\t *\n\t * Enables/disables unit testing\n\t *\n\t * @param\tbool\n\t * @return\tvoid\n\t */\n\tpublic function active($state = TRUE)\n\t{\n\t\t$this->active = (bool) $state;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Result Array\n\t *\n\t * Returns the raw result data\n\t *\n\t * @param\tarray\t$results\n\t * @return\tarray\n\t */\n\tpublic function result($results = array())\n\t{\n\t\t$CI =& get_instance();\n\t\t$CI->load->language('unit_test');\n\n\t\tif (count($results) === 0)\n\t\t{\n\t\t\t$results = $this->results;\n\t\t}\n\n\t\t$retval = array();\n\t\tforeach ($results as $result)\n\t\t{\n\t\t\t$temp = array();\n\t\t\tforeach ($result as $key => $val)\n\t\t\t{\n\t\t\t\tif ( ! in_array($key, $this->_test_items_visible))\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\telseif (in_array($key, array('test_name', 'test_datatype', 'res_datatype', 'result'), TRUE))\n\t\t\t\t{\n\t\t\t\t\tif (FALSE !== ($line = $CI->lang->line(strtolower('ut_'.$val), FALSE)))\n\t\t\t\t\t{\n\t\t\t\t\t\t$val = $line;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$temp[$CI->lang->line('ut_'.$key, FALSE)] = $val;\n\t\t\t}\n\n\t\t\t$retval[] = $temp;\n\t\t}\n\n\t\treturn $retval;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set the template\n\t *\n\t * This lets us set the template to be used to display results\n\t *\n\t * @param\tstring\n\t * @return\tvoid\n\t */\n\tpublic function set_template($template)\n\t{\n\t\t$this->_template = $template;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Generate a backtrace\n\t *\n\t * This lets us show file names and line numbers\n\t *\n\t * @return\tarray\n\t */\n\tprotected function _backtrace()\n\t{\n\t\t$back = debug_backtrace();\n\t\treturn array(\n\t\t\t'file' => (isset($back[1]['file']) ? $back[1]['file'] : ''),\n\t\t\t'line' => (isset($back[1]['line']) ? $back[1]['line'] : '')\n\t\t);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get Default Template\n\t *\n\t * @return\tstring\n\t */\n\tprotected function _default_template()\n\t{\n\t\t$this->_template = \"\\n\".'<table style=\"width:100%; font-size:small; margin:10px 0; border-collapse:collapse; border:1px solid #CCC;\">{rows}'.\"\\n</table>\";\n\n\t\t$this->_template_rows = \"\\n\\t<tr>\\n\\t\\t\".'<th style=\"text-align: left; border-bottom:1px solid #CCC;\">{item}</th>'\n\t\t\t\t\t.\"\\n\\t\\t\".'<td style=\"border-bottom:1px solid #CCC;\">{result}</td>'.\"\\n\\t</tr>\";\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Parse Template\n\t *\n\t * Harvests the data within the template {pseudo-variables}\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _parse_template()\n\t{\n\t\tif ($this->_template_rows !== NULL)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tif ($this->_template === NULL OR ! preg_match('/\\{rows\\}(.*?)\\{\\/rows\\}/si', $this->_template, $match))\n\t\t{\n\t\t\t$this->_default_template();\n\t\t\treturn;\n\t\t}\n\n\t\t$this->_template_rows = $match[1];\n\t\t$this->_template = str_replace($match[0], '{rows}', $this->_template);\n\t}\n\n}\n\n/**\n * Helper function to test boolean TRUE\n *\n * @param\tmixed\t$test\n * @return\tbool\n */\nfunction is_true($test)\n{\n\treturn ($test === TRUE);\n}\n\n/**\n * Helper function to test boolean FALSE\n *\n * @param\tmixed\t$test\n * @return\tbool\n */\nfunction is_false($test)\n{\n\treturn ($test === FALSE);\n}\n"
  },
  {
    "path": "system/libraries/Upload.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * File Uploading Class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tUploads\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/file_uploading.html\n */\nclass CI_Upload {\n\n\t/**\n\t * Maximum file size\n\t *\n\t * @var\tint\n\t */\n\tpublic $max_size = 0;\n\n\t/**\n\t * Maximum image width\n\t *\n\t * @var\tint\n\t */\n\tpublic $max_width = 0;\n\n\t/**\n\t * Maximum image height\n\t *\n\t * @var\tint\n\t */\n\tpublic $max_height = 0;\n\n\t/**\n\t * Minimum image width\n\t *\n\t * @var\tint\n\t */\n\tpublic $min_width = 0;\n\n\t/**\n\t * Minimum image height\n\t *\n\t * @var\tint\n\t */\n\tpublic $min_height = 0;\n\n\t/**\n\t * Maximum filename length\n\t *\n\t * @var\tint\n\t */\n\tpublic $max_filename = 0;\n\n\t/**\n\t * Maximum duplicate filename increment ID\n\t *\n\t * @var\tint\n\t */\n\tpublic $max_filename_increment = 100;\n\n\t/**\n\t * Allowed file types\n\t *\n\t * @var\tstring\n\t */\n\tpublic $allowed_types = '';\n\n\t/**\n\t * Temporary filename\n\t *\n\t * @var\tstring\n\t */\n\tpublic $file_temp = '';\n\n\t/**\n\t * Filename\n\t *\n\t * @var\tstring\n\t */\n\tpublic $file_name = '';\n\n\t/**\n\t * Original filename\n\t *\n\t * @var\tstring\n\t */\n\tpublic $orig_name = '';\n\n\t/**\n\t * File type\n\t *\n\t * @var\tstring\n\t */\n\tpublic $file_type = '';\n\n\t/**\n\t * File size\n\t *\n\t * @var\tint\n\t */\n\tpublic $file_size = NULL;\n\n\t/**\n\t * Filename extension\n\t *\n\t * @var\tstring\n\t */\n\tpublic $file_ext = '';\n\n\t/**\n\t * Force filename extension to lowercase\n\t *\n\t * @var\tstring\n\t */\n\tpublic $file_ext_tolower = FALSE;\n\n\t/**\n\t * Upload path\n\t *\n\t * @var\tstring\n\t */\n\tpublic $upload_path = '';\n\n\t/**\n\t * Overwrite flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $overwrite = FALSE;\n\n\t/**\n\t * Obfuscate filename flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $encrypt_name = FALSE;\n\n\t/**\n\t * Is image flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $is_image = FALSE;\n\n\t/**\n\t * Image width\n\t *\n\t * @var\tint\n\t */\n\tpublic $image_width = NULL;\n\n\t/**\n\t * Image height\n\t *\n\t * @var\tint\n\t */\n\tpublic $image_height = NULL;\n\n\t/**\n\t * Image type\n\t *\n\t * @var\tstring\n\t */\n\tpublic $image_type = '';\n\n\t/**\n\t * Image size string\n\t *\n\t * @var\tstring\n\t */\n\tpublic $image_size_str = '';\n\n\t/**\n\t * Error messages list\n\t *\n\t * @var\tarray\n\t */\n\tpublic $error_msg = array();\n\n\t/**\n\t * Remove spaces flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $remove_spaces = TRUE;\n\n\t/**\n\t * MIME detection flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $detect_mime = TRUE;\n\n\t/**\n\t * XSS filter flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $xss_clean = FALSE;\n\n\t/**\n\t * Apache mod_mime fix flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $mod_mime_fix = TRUE;\n\n\t/**\n\t * Temporary filename prefix\n\t *\n\t * @var\tstring\n\t */\n\tpublic $temp_prefix = 'temp_file_';\n\n\t/**\n\t * Filename sent by the client\n\t *\n\t * @var\tbool\n\t */\n\tpublic $client_name = '';\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Filename override\n\t *\n\t * @var\tstring\n\t */\n\tprotected $_file_name_override = '';\n\n\t/**\n\t * MIME types list\n\t *\n\t * @var\tarray\n\t */\n\tprotected $_mimes = array();\n\n\t/**\n\t * CI Singleton\n\t *\n\t * @var\tobject\n\t */\n\tprotected $_CI;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Constructor\n\t *\n\t * @param\tarray\t$config\n\t * @return\tvoid\n\t */\n\tpublic function __construct($config = array())\n\t{\n\t\tempty($config) OR $this->initialize($config, FALSE);\n\n\t\t$this->_mimes =& get_mimes();\n\t\t$this->_CI =& get_instance();\n\n\t\tlog_message('info', 'Upload Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Initialize preferences\n\t *\n\t * @param\tarray\t$config\n\t * @param\tbool\t$reset\n\t * @return\tCI_Upload\n\t */\n\tpublic function initialize(array $config = array(), $reset = TRUE)\n\t{\n\t\t$reflection = new ReflectionClass($this);\n\n\t\tif ($reset === TRUE)\n\t\t{\n\t\t\t$defaults = $reflection->getDefaultProperties();\n\t\t\tforeach (array_keys($defaults) as $key)\n\t\t\t{\n\t\t\t\tif ($key[0] === '_')\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (isset($config[$key]))\n\t\t\t\t{\n\t\t\t\t\tif ($reflection->hasMethod('set_'.$key))\n\t\t\t\t\t{\n\t\t\t\t\t\t$this->{'set_'.$key}($config[$key]);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t$this->$key = $config[$key];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$this->$key = $defaults[$key];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach ($config as $key => &$value)\n\t\t\t{\n\t\t\t\tif ($key[0] !== '_' && $reflection->hasProperty($key))\n\t\t\t\t{\n\t\t\t\t\tif ($reflection->hasMethod('set_'.$key))\n\t\t\t\t\t{\n\t\t\t\t\t\t$this->{'set_'.$key}($value);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t$this->$key = $value;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// if a file_name was provided in the config, use it instead of the user input\n\t\t// supplied file name for all uploads until initialized again\n\t\t$this->_file_name_override = $this->file_name;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Perform the file upload\n\t *\n\t * @param\tstring\t$field\n\t * @return\tbool\n\t */\n\tpublic function do_upload($field = 'userfile')\n\t{\n\t\t// Is $_FILES[$field] set? If not, no reason to continue.\n\t\tif (isset($_FILES[$field]))\n\t\t{\n\t\t\t$_file = $_FILES[$field];\n\t\t}\n\t\t// Does the field name contain array notation?\n\t\telseif (($c = preg_match_all('/(?:^[^\\[]+)|\\[[^]]*\\]/', $field, $matches)) > 1)\n\t\t{\n\t\t\t$_file = $_FILES;\n\t\t\tfor ($i = 0; $i < $c; $i++)\n\t\t\t{\n\t\t\t\t// We can't track numeric iterations, only full field names are accepted\n\t\t\t\tif (($field = trim($matches[0][$i], '[]')) === '' OR ! isset($_file[$field]))\n\t\t\t\t{\n\t\t\t\t\t$_file = NULL;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t$_file = $_file[$field];\n\t\t\t}\n\t\t}\n\n\t\tif ( ! isset($_file))\n\t\t{\n\t\t\t$this->set_error('upload_no_file_selected', 'debug');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Is the upload path valid?\n\t\tif ( ! $this->validate_upload_path())\n\t\t{\n\t\t\t// errors will already be set by validate_upload_path() so just return FALSE\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Was the file able to be uploaded? If not, determine the reason why.\n\t\tif ( ! is_uploaded_file($_file['tmp_name']))\n\t\t{\n\t\t\t$error = isset($_file['error']) ? $_file['error'] : 4;\n\n\t\t\tswitch ($error)\n\t\t\t{\n\t\t\t\tcase UPLOAD_ERR_INI_SIZE:\n\t\t\t\t\t$this->set_error('upload_file_exceeds_limit', 'info');\n\t\t\t\t\tbreak;\n\t\t\t\tcase UPLOAD_ERR_FORM_SIZE:\n\t\t\t\t\t$this->set_error('upload_file_exceeds_form_limit', 'info');\n\t\t\t\t\tbreak;\n\t\t\t\tcase UPLOAD_ERR_PARTIAL:\n\t\t\t\t\t$this->set_error('upload_file_partial', 'debug');\n\t\t\t\t\tbreak;\n\t\t\t\tcase UPLOAD_ERR_NO_FILE:\n\t\t\t\t\t$this->set_error('upload_no_file_selected', 'debug');\n\t\t\t\t\tbreak;\n\t\t\t\tcase UPLOAD_ERR_NO_TMP_DIR:\n\t\t\t\t\t$this->set_error('upload_no_temp_directory', 'error');\n\t\t\t\t\tbreak;\n\t\t\t\tcase UPLOAD_ERR_CANT_WRITE:\n\t\t\t\t\t$this->set_error('upload_unable_to_write_file', 'error');\n\t\t\t\t\tbreak;\n\t\t\t\tcase UPLOAD_ERR_EXTENSION:\n\t\t\t\t\t$this->set_error('upload_stopped_by_extension', 'debug');\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\t$this->set_error('upload_no_file_selected', 'debug');\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Set the uploaded data as class variables\n\t\t$this->file_temp = $_file['tmp_name'];\n\t\t$this->file_size = $_file['size'];\n\n\t\t// Skip MIME type detection?\n\t\tif ($this->detect_mime !== FALSE)\n\t\t{\n\t\t\t$this->_file_mime_type($_file);\n\t\t}\n\n\t\t$this->file_type = preg_replace('/^(.+?);.*$/', '\\\\1', $this->file_type);\n\t\t$this->file_type = strtolower(trim(stripslashes($this->file_type), '\"'));\n\t\t$this->file_name = $this->_prep_filename($_file['name']);\n\t\t$this->file_ext\t = $this->get_extension($this->file_name);\n\t\t$this->client_name = $this->file_name;\n\n\t\t// Is the file type allowed to be uploaded?\n\t\tif ( ! $this->is_allowed_filetype())\n\t\t{\n\t\t\t$this->set_error('upload_invalid_filetype', 'debug');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// if we're overriding, let's now make sure the new name and type is allowed\n\t\tif ($this->_file_name_override !== '')\n\t\t{\n\t\t\t$this->file_name = $this->_prep_filename($this->_file_name_override);\n\n\t\t\t// If no extension was provided in the file_name config item, use the uploaded one\n\t\t\tif (strpos($this->_file_name_override, '.') === FALSE)\n\t\t\t{\n\t\t\t\t$this->file_name .= $this->file_ext;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// An extension was provided, let's have it!\n\t\t\t\t$this->file_ext\t= $this->get_extension($this->_file_name_override);\n\t\t\t}\n\n\t\t\tif ( ! $this->is_allowed_filetype(TRUE))\n\t\t\t{\n\t\t\t\t$this->set_error('upload_invalid_filetype', 'debug');\n\t\t\t\treturn FALSE;\n\t\t\t}\n\t\t}\n\n\t\t// Convert the file size to kilobytes\n\t\tif ($this->file_size > 0)\n\t\t{\n\t\t\t$this->file_size = round($this->file_size/1024, 2);\n\t\t}\n\n\t\t// Is the file size within the allowed maximum?\n\t\tif ( ! $this->is_allowed_filesize())\n\t\t{\n\t\t\t$this->set_error('upload_invalid_filesize', 'info');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Are the image dimensions within the allowed size?\n\t\t// Note: This can fail if the server has an open_basedir restriction.\n\t\tif ( ! $this->is_allowed_dimensions())\n\t\t{\n\t\t\t$this->set_error('upload_invalid_dimensions', 'info');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Sanitize the file name for security\n\t\t$this->file_name = $this->_CI->security->sanitize_filename($this->file_name);\n\n\t\t// Truncate the file name if it's too long\n\t\tif ($this->max_filename > 0)\n\t\t{\n\t\t\t$this->file_name = $this->limit_filename_length($this->file_name, $this->max_filename);\n\t\t}\n\n\t\t// Remove white spaces in the name\n\t\tif ($this->remove_spaces === TRUE)\n\t\t{\n\t\t\t$this->file_name = preg_replace('/\\s+/', '_', $this->file_name);\n\t\t}\n\n\t\tif ($this->file_ext_tolower && ($ext_length = strlen($this->file_ext)))\n\t\t{\n\t\t\t// file_ext was previously lower-cased by a get_extension() call\n\t\t\t$this->file_name = substr($this->file_name, 0, -$ext_length).$this->file_ext;\n\t\t}\n\n\t\t/*\n\t\t * Validate the file name\n\t\t * This function appends an number onto the end of\n\t\t * the file if one with the same name already exists.\n\t\t * If it returns false there was a problem.\n\t\t */\n\t\t$this->orig_name = $this->file_name;\n\t\tif (FALSE === ($this->file_name = $this->set_filename($this->upload_path, $this->file_name)))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t/*\n\t\t * Run the file through the XSS hacking filter\n\t\t * This helps prevent malicious code from being\n\t\t * embedded within a file. Scripts can easily\n\t\t * be disguised as images or other file types.\n\t\t */\n\t\tif ($this->xss_clean && $this->do_xss_clean() === FALSE)\n\t\t{\n\t\t\t$this->set_error('upload_unable_to_write_file', 'error');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t/*\n\t\t * Move the file to the final destination\n\t\t * To deal with different server configurations\n\t\t * we'll attempt to use copy() first. If that fails\n\t\t * we'll use move_uploaded_file(). One of the two should\n\t\t * reliably work in most environments\n\t\t */\n\t\tif ( ! @copy($this->file_temp, $this->upload_path.$this->file_name))\n\t\t{\n\t\t\tif ( ! @move_uploaded_file($this->file_temp, $this->upload_path.$this->file_name))\n\t\t\t{\n\t\t\t\t$this->set_error('upload_destination_error', 'error');\n\t\t\t\treturn FALSE;\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * Set the finalized image dimensions\n\t\t * This sets the image width/height (assuming the\n\t\t * file was an image). We use this information\n\t\t * in the \"data\" function.\n\t\t */\n\t\t$this->set_image_properties($this->upload_path.$this->file_name);\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Finalized Data Array\n\t *\n\t * Returns an associative array containing all of the information\n\t * related to the upload, allowing the developer easy access in one array.\n\t *\n\t * @param\tstring\t$index\n\t * @return\tmixed\n\t */\n\tpublic function data($index = NULL)\n\t{\n\t\t$data = array(\n\t\t\t\t'file_name'\t\t=> $this->file_name,\n\t\t\t\t'file_type'\t\t=> $this->file_type,\n\t\t\t\t'file_path'\t\t=> $this->upload_path,\n\t\t\t\t'full_path'\t\t=> $this->upload_path.$this->file_name,\n\t\t\t\t'raw_name'\t\t=> substr($this->file_name, 0, -strlen($this->file_ext)),\n\t\t\t\t'orig_name'\t\t=> $this->orig_name,\n\t\t\t\t'client_name'\t\t=> $this->client_name,\n\t\t\t\t'file_ext'\t\t=> $this->file_ext,\n\t\t\t\t'file_size'\t\t=> $this->file_size,\n\t\t\t\t'is_image'\t\t=> $this->is_image(),\n\t\t\t\t'image_width'\t\t=> $this->image_width,\n\t\t\t\t'image_height'\t\t=> $this->image_height,\n\t\t\t\t'image_type'\t\t=> $this->image_type,\n\t\t\t\t'image_size_str'\t=> $this->image_size_str,\n\t\t\t);\n\n\t\tif ( ! empty($index))\n\t\t{\n\t\t\treturn isset($data[$index]) ? $data[$index] : NULL;\n\t\t}\n\n\t\treturn $data;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Upload Path\n\t *\n\t * @param\tstring\t$path\n\t * @return\tCI_Upload\n\t */\n\tpublic function set_upload_path($path)\n\t{\n\t\t// Make sure it has a trailing slash\n\t\t$this->upload_path = rtrim($path, '/').'/';\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set the file name\n\t *\n\t * This function takes a filename/path as input and looks for the\n\t * existence of a file with the same name. If found, it will append a\n\t * number to the end of the filename to avoid overwriting a pre-existing file.\n\t *\n\t * @param\tstring\t$path\n\t * @param\tstring\t$filename\n\t * @return\tstring\n\t */\n\tpublic function set_filename($path, $filename)\n\t{\n\t\tif ($this->encrypt_name === TRUE)\n\t\t{\n\t\t\t$filename = md5(uniqid(mt_rand())).$this->file_ext;\n\t\t}\n\n\t\tif ($this->overwrite === TRUE OR ! file_exists($path.$filename))\n\t\t{\n\t\t\treturn $filename;\n\t\t}\n\n\t\t$filename = str_replace($this->file_ext, '', $filename);\n\n\t\t$new_filename = '';\n\t\tfor ($i = 1; $i < $this->max_filename_increment; $i++)\n\t\t{\n\t\t\tif ( ! file_exists($path.$filename.$i.$this->file_ext))\n\t\t\t{\n\t\t\t\t$new_filename = $filename.$i.$this->file_ext;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif ($new_filename === '')\n\t\t{\n\t\t\t$this->set_error('upload_bad_filename', 'debug');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn $new_filename;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Maximum File Size\n\t *\n\t * @param\tint\t$n\n\t * @return\tCI_Upload\n\t */\n\tpublic function set_max_filesize($n)\n\t{\n\t\t$this->max_size = ($n < 0) ? 0 : (int) $n;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Maximum File Size\n\t *\n\t * An internal alias to set_max_filesize() to help with configuration\n\t * as initialize() will look for a set_<property_name>() method ...\n\t *\n\t * @param\tint\t$n\n\t * @return\tCI_Upload\n\t */\n\tprotected function set_max_size($n)\n\t{\n\t\treturn $this->set_max_filesize($n);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Maximum File Name Length\n\t *\n\t * @param\tint\t$n\n\t * @return\tCI_Upload\n\t */\n\tpublic function set_max_filename($n)\n\t{\n\t\t$this->max_filename = ($n < 0) ? 0 : (int) $n;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Maximum Image Width\n\t *\n\t * @param\tint\t$n\n\t * @return\tCI_Upload\n\t */\n\tpublic function set_max_width($n)\n\t{\n\t\t$this->max_width = ($n < 0) ? 0 : (int) $n;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Maximum Image Height\n\t *\n\t * @param\tint\t$n\n\t * @return\tCI_Upload\n\t */\n\tpublic function set_max_height($n)\n\t{\n\t\t$this->max_height = ($n < 0) ? 0 : (int) $n;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set minimum image width\n\t *\n\t * @param\tint\t$n\n\t * @return\tCI_Upload\n\t */\n\tpublic function set_min_width($n)\n\t{\n\t\t$this->min_width = ($n < 0) ? 0 : (int) $n;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set minimum image height\n\t *\n\t * @param\tint\t$n\n\t * @return\tCI_Upload\n\t */\n\tpublic function set_min_height($n)\n\t{\n\t\t$this->min_height = ($n < 0) ? 0 : (int) $n;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Allowed File Types\n\t *\n\t * @param\tmixed\t$types\n\t * @return\tCI_Upload\n\t */\n\tpublic function set_allowed_types($types)\n\t{\n\t\t$this->allowed_types = (is_array($types) OR $types === '*')\n\t\t\t? $types\n\t\t\t: explode('|', $types);\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Image Properties\n\t *\n\t * Uses GD to determine the width/height/type of image\n\t *\n\t * @param\tstring\t$path\n\t * @return\tCI_Upload\n\t */\n\tpublic function set_image_properties($path = '')\n\t{\n\t\tif ($this->is_image() && function_exists('getimagesize'))\n\t\t{\n\t\t\tif (FALSE !== ($D = @getimagesize($path)))\n\t\t\t{\n\t\t\t\t$types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png');\n\n\t\t\t\t$this->image_width\t= $D[0];\n\t\t\t\t$this->image_height\t= $D[1];\n\t\t\t\t$this->image_type\t= isset($types[$D[2]]) ? $types[$D[2]] : 'unknown';\n\t\t\t\t$this->image_size_str\t= $D[3]; // string containing height and width\n\t\t\t}\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set XSS Clean\n\t *\n\t * Enables the XSS flag so that the file that was uploaded\n\t * will be run through the XSS filter.\n\t *\n\t * @param\tbool\t$flag\n\t * @return\tCI_Upload\n\t */\n\tpublic function set_xss_clean($flag = FALSE)\n\t{\n\t\t$this->xss_clean = ($flag === TRUE);\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Validate the image\n\t *\n\t * @return\tbool\n\t */\n\tpublic function is_image()\n\t{\n\t\t// IE will sometimes return odd mime-types during upload, so here we just standardize all\n\t\t// jpegs or pngs to the same file type.\n\n\t\t$png_mimes  = array('image/x-png');\n\t\t$jpeg_mimes = array('image/jpg', 'image/jpe', 'image/jpeg', 'image/pjpeg');\n\n\t\tif (in_array($this->file_type, $png_mimes))\n\t\t{\n\t\t\t$this->file_type = 'image/png';\n\t\t}\n\t\telseif (in_array($this->file_type, $jpeg_mimes))\n\t\t{\n\t\t\t$this->file_type = 'image/jpeg';\n\t\t}\n\n\t\t$img_mimes = array('image/gif',\t'image/jpeg', 'image/png', 'image/webp');\n\n\t\treturn in_array($this->file_type, $img_mimes, TRUE);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Verify that the filetype is allowed\n\t *\n\t * @param\tbool\t$ignore_mime\n\t * @return\tbool\n\t */\n\tpublic function is_allowed_filetype($ignore_mime = FALSE)\n\t{\n\t\tif ($this->allowed_types === '*')\n\t\t{\n\t\t\treturn TRUE;\n\t\t}\n\n\t\tif (empty($this->allowed_types) OR ! is_array($this->allowed_types))\n\t\t{\n\t\t\t$this->set_error('upload_no_file_types', 'debug');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$ext = strtolower(ltrim($this->file_ext, '.'));\n\n\t\tif ( ! in_array($ext, $this->allowed_types, TRUE))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Images get some additional checks\n\t\tif (in_array($ext, array('gif', 'jpg', 'jpeg', 'jpe', 'png', 'webp'), TRUE) && @getimagesize($this->file_temp) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif ($ignore_mime === TRUE)\n\t\t{\n\t\t\treturn TRUE;\n\t\t}\n\n\t\tif (isset($this->_mimes[$ext]))\n\t\t{\n\t\t\treturn is_array($this->_mimes[$ext])\n\t\t\t\t? in_array($this->file_type, $this->_mimes[$ext], TRUE)\n\t\t\t\t: ($this->_mimes[$ext] === $this->file_type);\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Verify that the file is within the allowed size\n\t *\n\t * @return\tbool\n\t */\n\tpublic function is_allowed_filesize()\n\t{\n\t\treturn ($this->max_size === 0 OR $this->max_size > $this->file_size);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Verify that the image is within the allowed width/height\n\t *\n\t * @return\tbool\n\t */\n\tpublic function is_allowed_dimensions()\n\t{\n\t\tif ( ! $this->is_image())\n\t\t{\n\t\t\treturn TRUE;\n\t\t}\n\n\t\tif (function_exists('getimagesize'))\n\t\t{\n\t\t\t$D = @getimagesize($this->file_temp);\n\n\t\t\tif ($this->max_width > 0 && $D[0] > $this->max_width)\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\tif ($this->max_height > 0 && $D[1] > $this->max_height)\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\tif ($this->min_width > 0 && $D[0] < $this->min_width)\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n\t\t\tif ($this->min_height > 0 && $D[1] < $this->min_height)\n\t\t\t{\n\t\t\t\treturn FALSE;\n\t\t\t}\n\t\t}\n\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Validate Upload Path\n\t *\n\t * Verifies that it is a valid upload path with proper permissions.\n\t *\n\t * @return\tbool\n\t */\n\tpublic function validate_upload_path()\n\t{\n\t\tif ($this->upload_path === '')\n\t\t{\n\t\t\t$this->set_error('upload_no_filepath', 'error');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif (realpath($this->upload_path) !== FALSE)\n\t\t{\n\t\t\t$this->upload_path = str_replace('\\\\', '/', realpath($this->upload_path));\n\t\t}\n\n\t\tif ( ! is_dir($this->upload_path))\n\t\t{\n\t\t\t$this->set_error('upload_no_filepath', 'error');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif ( ! is_really_writable($this->upload_path))\n\t\t{\n\t\t\t$this->set_error('upload_not_writable', 'error');\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$this->upload_path = preg_replace('/(.+?)\\/*$/', '\\\\1/',  $this->upload_path);\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Extract the file extension\n\t *\n\t * @param\tstring\t$filename\n\t * @return\tstring\n\t */\n\tpublic function get_extension($filename)\n\t{\n\t\t$x = explode('.', $filename);\n\n\t\tif (count($x) === 1)\n\t\t{\n\t\t\treturn '';\n\t\t}\n\n\t\t$ext = ($this->file_ext_tolower) ? strtolower(end($x)) : end($x);\n\t\treturn '.'.$ext;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Limit the File Name Length\n\t *\n\t * @param\tstring\t$filename\n\t * @param\tint\t$length\n\t * @return\tstring\n\t */\n\tpublic function limit_filename_length($filename, $length)\n\t{\n\t\tif (strlen($filename) < $length)\n\t\t{\n\t\t\treturn $filename;\n\t\t}\n\n\t\t$ext = '';\n\t\tif (strpos($filename, '.') !== FALSE)\n\t\t{\n\t\t\t$parts\t\t= explode('.', $filename);\n\t\t\t$ext\t\t= '.'.array_pop($parts);\n\t\t\t$filename\t= implode('.', $parts);\n\t\t}\n\n\t\treturn substr($filename, 0, ($length - strlen($ext))).$ext;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Runs the file through the XSS clean function\n\t *\n\t * This prevents people from embedding malicious code in their files.\n\t * I'm not sure that it won't negatively affect certain files in unexpected ways,\n\t * but so far I haven't found that it causes trouble.\n\t *\n\t * @return\tstring\n\t */\n\tpublic function do_xss_clean()\n\t{\n\t\t$file = $this->file_temp;\n\n\t\tif (filesize($file) == 0)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif (memory_get_usage() && ($memory_limit = ini_get('memory_limit')) > 0)\n\t\t{\n\t\t\t$memory_limit = str_split($memory_limit, strspn($memory_limit, '1234567890'));\n\t\t\tif ( ! empty($memory_limit[1]))\n\t\t\t{\n\t\t\t\tswitch ($memory_limit[1][0])\n\t\t\t\t{\n\t\t\t\t\tcase 'g':\n\t\t\t\t\tcase 'G':\n\t\t\t\t\t\t$memory_limit[0] *= 1024 * 1024 * 1024;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'm':\n\t\t\t\t\tcase 'M':\n\t\t\t\t\t\t$memory_limit[0] *= 1024 * 1024;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$memory_limit = (int) ceil(filesize($file) + $memory_limit[0]);\n\t\t\tini_set('memory_limit', $memory_limit); // When an integer is used, the value is measured in bytes. - PHP.net\n\t\t}\n\n\t\t// If the file being uploaded is an image, then we should have no problem with XSS attacks (in theory), but\n\t\t// IE can be fooled into mime-type detecting a malformed image as an html file, thus executing an XSS attack on anyone\n\t\t// using IE who looks at the image. It does this by inspecting the first 255 bytes of an image. To get around this\n\t\t// CI will itself look at the first 255 bytes of an image to determine its relative safety. This can save a lot of\n\t\t// processor power and time if it is actually a clean image, as it will be in nearly all instances _except_ an\n\t\t// attempted XSS attack.\n\n\t\tif (function_exists('getimagesize') && @getimagesize($file) !== FALSE)\n\t\t{\n\t\t\tif (($file = @fopen($file, 'rb')) === FALSE) // \"b\" to force binary\n\t\t\t{\n\t\t\t\treturn FALSE; // Couldn't open the file, return FALSE\n\t\t\t}\n\n\t\t\t$opening_bytes = fread($file, 256);\n\t\t\tfclose($file);\n\n\t\t\t// These are known to throw IE into mime-type detection chaos\n\t\t\t// <a, <body, <head, <html, <img, <plaintext, <pre, <script, <table, <title\n\t\t\t// title is basically just in SVG, but we filter it anyhow\n\n\t\t\t// if it's an image or no \"triggers\" detected in the first 256 bytes - we're good\n\t\t\treturn ! preg_match('/<(a|body|head|html|img|plaintext|pre|script|table|title)[\\s>]/i', $opening_bytes);\n\t\t}\n\n\t\tif (($data = @file_get_contents($file)) === FALSE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\treturn $this->_CI->security->xss_clean($data, TRUE);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set an error message\n\t *\n\t * @param\tstring\t$msg\n\t * @return\tCI_Upload\n\t */\n\tpublic function set_error($msg, $log_level = 'error')\n\t{\n\t\t$this->_CI->lang->load('upload');\n\n\t\tis_array($msg) OR $msg = array($msg);\n\t\tforeach ($msg as $val)\n\t\t{\n\t\t\t$msg = ($this->_CI->lang->line($val) === FALSE) ? $val : $this->_CI->lang->line($val);\n\t\t\t$this->error_msg[] = $msg;\n\t\t\tlog_message($log_level, $msg);\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Display the error message\n\t *\n\t * @param\tstring\t$open\n\t * @param\tstring\t$close\n\t * @return\tstring\n\t */\n\tpublic function display_errors($open = '<p>', $close = '</p>')\n\t{\n\t\treturn (count($this->error_msg) > 0) ? $open.implode($close.$open, $this->error_msg).$close : '';\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Prep Filename\n\t *\n\t * Prevents possible script execution from Apache's handling\n\t * of files' multiple extensions.\n\t *\n\t * @link\thttps://httpd.apache.org/docs/1.3/mod/mod_mime.html#multipleext\n\t *\n\t * @param\tstring\t$filename\n\t * @return\tstring\n\t */\n\tprotected function _prep_filename($filename)\n\t{\n\t\tif ($this->mod_mime_fix === FALSE OR $this->allowed_types === '*' OR ($ext_pos = strrpos($filename, '.')) === FALSE)\n\t\t{\n\t\t\treturn $filename;\n\t\t}\n\n\t\t$ext = substr($filename, $ext_pos);\n\t\t$filename = substr($filename, 0, $ext_pos);\n\t\treturn str_replace('.', '_', $filename).$ext;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * File MIME type\n\t *\n\t * Detects the (actual) MIME type of the uploaded file, if possible.\n\t * The input array is expected to be $_FILES[$field]\n\t *\n\t * @param\tarray\t$file\n\t * @return\tvoid\n\t */\n\tprotected function _file_mime_type($file)\n\t{\n\t\t// We'll need this to validate the MIME info string (e.g. text/plain; charset=us-ascii)\n\t\t$regexp = '/^([a-z\\-]+\\/[a-z0-9\\-\\.\\+]+)(;\\s.+)?$/';\n\n\t\t/**\n\t\t * Fileinfo extension - most reliable method\n\t\t *\n\t\t * Apparently XAMPP, CentOS, cPanel and who knows what\n\t\t * other PHP distribution channels EXPLICITLY DISABLE\n\t\t * ext/fileinfo, which is otherwise enabled by default\n\t\t * since PHP 5.3 ...\n\t\t */\n\t\tif (function_exists('finfo_file'))\n\t\t{\n\t\t\t$finfo = @finfo_open(FILEINFO_MIME);\n\t\t\tif ($finfo !== FALSE) // It is possible that a FALSE value is returned, if there is no magic MIME database file found on the system\n\t\t\t{\n\t\t\t\t$mime = @finfo_file($finfo, $file['tmp_name']);\n\t\t\t\tfinfo_close($finfo);\n\n\t\t\t\t/* According to the comments section of the PHP manual page,\n\t\t\t\t * it is possible that this function returns an empty string\n\t\t\t\t * for some files (e.g. if they don't exist in the magic MIME database)\n\t\t\t\t */\n\t\t\t\tif (is_string($mime) && preg_match($regexp, $mime, $matches))\n\t\t\t\t{\n\t\t\t\t\t$this->file_type = $matches[1];\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/* This is an ugly hack, but UNIX-type systems provide a \"native\" way to detect the file type,\n\t\t * which is still more secure than depending on the value of $_FILES[$field]['type'], and as it\n\t\t * was reported in issue #750 (https://github.com/EllisLab/CodeIgniter/issues/750) - it's better\n\t\t * than mime_content_type() as well, hence the attempts to try calling the command line with\n\t\t * three different functions.\n\t\t *\n\t\t * Notes:\n\t\t *\t- the DIRECTORY_SEPARATOR comparison ensures that we're not on a Windows system\n\t\t *\t- many system admins would disable the exec(), shell_exec(), popen() and similar functions\n\t\t *\t  due to security concerns, hence the function_usable() checks\n\t\t */\n\t\tif (DIRECTORY_SEPARATOR !== '\\\\')\n\t\t{\n\t\t\t$cmd = 'file --brief --mime '.escapeshellarg($file['tmp_name']).' 2>&1';\n\n\t\t\tif (function_usable('exec'))\n\t\t\t{\n\t\t\t\t/* This might look confusing, as $mime is being populated with all of the output when set in the second parameter.\n\t\t\t\t * However, we only need the last line, which is the actual return value of exec(), and as such - it overwrites\n\t\t\t\t * anything that could already be set for $mime previously. This effectively makes the second parameter a dummy\n\t\t\t\t * value, which is only put to allow us to get the return status code.\n\t\t\t\t */\n\t\t\t\t$mime = @exec($cmd, $mime, $return_status);\n\t\t\t\tif ($return_status === 0 && is_string($mime) && preg_match($regexp, $mime, $matches))\n\t\t\t\t{\n\t\t\t\t\t$this->file_type = $matches[1];\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (function_usable('shell_exec'))\n\t\t\t{\n\t\t\t\t$mime = @shell_exec($cmd);\n\t\t\t\tif (strlen($mime) > 0)\n\t\t\t\t{\n\t\t\t\t\t$mime = explode(\"\\n\", trim($mime));\n\t\t\t\t\tif (preg_match($regexp, $mime[(count($mime) - 1)], $matches))\n\t\t\t\t\t{\n\t\t\t\t\t\t$this->file_type = $matches[1];\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (function_usable('popen'))\n\t\t\t{\n\t\t\t\t$proc = @popen($cmd, 'r');\n\t\t\t\tif (is_resource($proc))\n\t\t\t\t{\n\t\t\t\t\t$mime = @fread($proc, 512);\n\t\t\t\t\t@pclose($proc);\n\t\t\t\t\tif ($mime !== FALSE)\n\t\t\t\t\t{\n\t\t\t\t\t\t$mime = explode(\"\\n\", trim($mime));\n\t\t\t\t\t\tif (preg_match($regexp, $mime[(count($mime) - 1)], $matches))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t$this->file_type = $matches[1];\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Fall back to mime_content_type(), if available (still better than $_FILES[$field]['type'])\n\t\tif (function_exists('mime_content_type'))\n\t\t{\n\t\t\t$this->file_type = @mime_content_type($file['tmp_name']);\n\t\t\tif (strlen($this->file_type) > 0) // It's possible that mime_content_type() returns FALSE or an empty string\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t$this->file_type = $file['type'];\n\t}\n\n}\n"
  },
  {
    "path": "system/libraries/User_agent.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * User Agent Class\n *\n * Identifies the platform, browser, robot, or mobile device of the browsing agent\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tUser Agent\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/user_agent.html\n */\nclass CI_User_agent {\n\n\t/**\n\t * Current user-agent\n\t *\n\t * @var string\n\t */\n\tpublic $agent = NULL;\n\n\t/**\n\t * Flag for if the user-agent belongs to a browser\n\t *\n\t * @var bool\n\t */\n\tpublic $is_browser = FALSE;\n\n\t/**\n\t * Flag for if the user-agent is a robot\n\t *\n\t * @var bool\n\t */\n\tpublic $is_robot = FALSE;\n\n\t/**\n\t * Flag for if the user-agent is a mobile browser\n\t *\n\t * @var bool\n\t */\n\tpublic $is_mobile = FALSE;\n\n\t/**\n\t * Languages accepted by the current user agent\n\t *\n\t * @var array\n\t */\n\tpublic $languages = array();\n\n\t/**\n\t * Character sets accepted by the current user agent\n\t *\n\t * @var array\n\t */\n\tpublic $charsets = array();\n\n\t/**\n\t * List of platforms to compare against current user agent\n\t *\n\t * @var array\n\t */\n\tpublic $platforms = array();\n\n\t/**\n\t * List of browsers to compare against current user agent\n\t *\n\t * @var array\n\t */\n\tpublic $browsers = array();\n\n\t/**\n\t * List of mobile browsers to compare against current user agent\n\t *\n\t * @var array\n\t */\n\tpublic $mobiles = array();\n\n\t/**\n\t * List of robots to compare against current user agent\n\t *\n\t * @var array\n\t */\n\tpublic $robots = array();\n\n\t/**\n\t * Current user-agent platform\n\t *\n\t * @var string\n\t */\n\tpublic $platform = '';\n\n\t/**\n\t * Current user-agent browser\n\t *\n\t * @var string\n\t */\n\tpublic $browser = '';\n\n\t/**\n\t * Current user-agent version\n\t *\n\t * @var string\n\t */\n\tpublic $version = '';\n\n\t/**\n\t * Current user-agent mobile name\n\t *\n\t * @var string\n\t */\n\tpublic $mobile = '';\n\n\t/**\n\t * Current user-agent robot name\n\t *\n\t * @var string\n\t */\n\tpublic $robot = '';\n\n\t/**\n\t * HTTP Referer\n\t *\n\t * @var\tmixed\n\t */\n\tpublic $referer;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Constructor\n\t *\n\t * Sets the User Agent and runs the compilation routine\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __construct()\n\t{\n\t\t$this->_load_agent_file();\n\n\t\tif (isset($_SERVER['HTTP_USER_AGENT']))\n\t\t{\n\t\t\t$this->agent = trim($_SERVER['HTTP_USER_AGENT']);\n\t\t\t$this->_compile_data();\n\t\t}\n\n\t\tlog_message('info', 'User Agent Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Compile the User Agent Data\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _load_agent_file()\n\t{\n\t\tif (($found = file_exists(APPPATH.'config/user_agents.php')))\n\t\t{\n\t\t\tinclude(APPPATH.'config/user_agents.php');\n\t\t}\n\n\t\tif (file_exists(APPPATH.'config/'.ENVIRONMENT.'/user_agents.php'))\n\t\t{\n\t\t\tinclude(APPPATH.'config/'.ENVIRONMENT.'/user_agents.php');\n\t\t\t$found = TRUE;\n\t\t}\n\n\t\tif ($found !== TRUE)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$return = FALSE;\n\n\t\tif (isset($platforms))\n\t\t{\n\t\t\t$this->platforms = $platforms;\n\t\t\tunset($platforms);\n\t\t\t$return = TRUE;\n\t\t}\n\n\t\tif (isset($browsers))\n\t\t{\n\t\t\t$this->browsers = $browsers;\n\t\t\tunset($browsers);\n\t\t\t$return = TRUE;\n\t\t}\n\n\t\tif (isset($mobiles))\n\t\t{\n\t\t\t$this->mobiles = $mobiles;\n\t\t\tunset($mobiles);\n\t\t\t$return = TRUE;\n\t\t}\n\n\t\tif (isset($robots))\n\t\t{\n\t\t\t$this->robots = $robots;\n\t\t\tunset($robots);\n\t\t\t$return = TRUE;\n\t\t}\n\n\t\treturn $return;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Compile the User Agent Data\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _compile_data()\n\t{\n\t\t$this->_set_platform();\n\n\t\tforeach (array('_set_robot', '_set_browser', '_set_mobile') as $function)\n\t\t{\n\t\t\tif ($this->$function() === TRUE)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set the Platform\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _set_platform()\n\t{\n\t\tif (is_array($this->platforms) && count($this->platforms) > 0)\n\t\t{\n\t\t\tforeach ($this->platforms as $key => $val)\n\t\t\t{\n\t\t\t\tif (preg_match('|'.preg_quote($key).'|i', $this->agent))\n\t\t\t\t{\n\t\t\t\t\t$this->platform = $val;\n\t\t\t\t\treturn TRUE;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$this->platform = 'Unknown Platform';\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set the Browser\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _set_browser()\n\t{\n\t\tif (is_array($this->browsers) && count($this->browsers) > 0)\n\t\t{\n\t\t\tforeach ($this->browsers as $key => $val)\n\t\t\t{\n\t\t\t\tif (preg_match('|'.$key.'.*?([0-9\\.]+)|i', $this->agent, $match))\n\t\t\t\t{\n\t\t\t\t\t$this->is_browser = TRUE;\n\t\t\t\t\t$this->version = $match[1];\n\t\t\t\t\t$this->browser = $val;\n\t\t\t\t\t$this->_set_mobile();\n\t\t\t\t\treturn TRUE;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set the Robot\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _set_robot()\n\t{\n\t\tif (is_array($this->robots) && count($this->robots) > 0)\n\t\t{\n\t\t\tforeach ($this->robots as $key => $val)\n\t\t\t{\n\t\t\t\tif (preg_match('|'.preg_quote($key).'|i', $this->agent))\n\t\t\t\t{\n\t\t\t\t\t$this->is_robot = TRUE;\n\t\t\t\t\t$this->robot = $val;\n\t\t\t\t\t$this->_set_mobile();\n\t\t\t\t\treturn TRUE;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set the Mobile Device\n\t *\n\t * @return\tbool\n\t */\n\tprotected function _set_mobile()\n\t{\n\t\tif (is_array($this->mobiles) && count($this->mobiles) > 0)\n\t\t{\n\t\t\tforeach ($this->mobiles as $key => $val)\n\t\t\t{\n\t\t\t\tif (FALSE !== (stripos($this->agent, $key)))\n\t\t\t\t{\n\t\t\t\t\t$this->is_mobile = TRUE;\n\t\t\t\t\t$this->mobile = $val;\n\t\t\t\t\treturn TRUE;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set the accepted languages\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _set_languages()\n\t{\n\t\tif ((count($this->languages) === 0) && ! empty($_SERVER['HTTP_ACCEPT_LANGUAGE']))\n\t\t{\n\t\t\t$this->languages = explode(',', preg_replace('/(;\\s?q=[0-9\\.]+)|\\s/i', '', strtolower(trim($_SERVER['HTTP_ACCEPT_LANGUAGE']))));\n\t\t}\n\n\t\tif (count($this->languages) === 0)\n\t\t{\n\t\t\t$this->languages = array('Undefined');\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set the accepted character sets\n\t *\n\t * @return\tvoid\n\t */\n\tprotected function _set_charsets()\n\t{\n\t\tif ((count($this->charsets) === 0) && ! empty($_SERVER['HTTP_ACCEPT_CHARSET']))\n\t\t{\n\t\t\t$this->charsets = explode(',', preg_replace('/(;\\s?q=.+)|\\s/i', '', strtolower(trim($_SERVER['HTTP_ACCEPT_CHARSET']))));\n\t\t}\n\n\t\tif (count($this->charsets) === 0)\n\t\t{\n\t\t\t$this->charsets = array('Undefined');\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Is Browser\n\t *\n\t * @param\tstring\t$key\n\t * @return\tbool\n\t */\n\tpublic function is_browser($key = NULL)\n\t{\n\t\tif ( ! $this->is_browser)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// No need to be specific, it's a browser\n\t\tif ($key === NULL)\n\t\t{\n\t\t\treturn TRUE;\n\t\t}\n\n\t\t// Check for a specific browser\n\t\treturn (isset($this->browsers[$key]) && $this->browser === $this->browsers[$key]);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Is Robot\n\t *\n\t * @param\tstring\t$key\n\t * @return\tbool\n\t */\n\tpublic function is_robot($key = NULL)\n\t{\n\t\tif ( ! $this->is_robot)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// No need to be specific, it's a robot\n\t\tif ($key === NULL)\n\t\t{\n\t\t\treturn TRUE;\n\t\t}\n\n\t\t// Check for a specific robot\n\t\treturn (isset($this->robots[$key]) && $this->robot === $this->robots[$key]);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Is Mobile\n\t *\n\t * @param\tstring\t$key\n\t * @return\tbool\n\t */\n\tpublic function is_mobile($key = NULL)\n\t{\n\t\tif ( ! $this->is_mobile)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// No need to be specific, it's a mobile\n\t\tif ($key === NULL)\n\t\t{\n\t\t\treturn TRUE;\n\t\t}\n\n\t\t// Check for a specific robot\n\t\treturn (isset($this->mobiles[$key]) && $this->mobile === $this->mobiles[$key]);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Is this a referral from another site?\n\t *\n\t * @return\tbool\n\t */\n\tpublic function is_referral()\n\t{\n\t\tif ( ! isset($this->referer))\n\t\t{\n\t\t\tif (empty($_SERVER['HTTP_REFERER']))\n\t\t\t{\n\t\t\t\t$this->referer = FALSE;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$referer_host = @parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST);\n\t\t\t\t$own_host = parse_url((string) config_item('base_url'), PHP_URL_HOST);\n\n\t\t\t\t$this->referer = ($referer_host && $referer_host !== $own_host);\n\t\t\t}\n\t\t}\n\n\t\treturn $this->referer;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Agent String\n\t *\n\t * @return\tstring\n\t */\n\tpublic function agent_string()\n\t{\n\t\treturn $this->agent;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get Platform\n\t *\n\t * @return\tstring\n\t */\n\tpublic function platform()\n\t{\n\t\treturn $this->platform;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get Browser Name\n\t *\n\t * @return\tstring\n\t */\n\tpublic function browser()\n\t{\n\t\treturn $this->browser;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get the Browser Version\n\t *\n\t * @return\tstring\n\t */\n\tpublic function version()\n\t{\n\t\treturn $this->version;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get The Robot Name\n\t *\n\t * @return\tstring\n\t */\n\tpublic function robot()\n\t{\n\t\treturn $this->robot;\n\t}\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get the Mobile Device\n\t *\n\t * @return\tstring\n\t */\n\tpublic function mobile()\n\t{\n\t\treturn $this->mobile;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get the referrer\n\t *\n\t * @return\tbool\n\t */\n\tpublic function referrer()\n\t{\n\t\treturn empty($_SERVER['HTTP_REFERER']) ? '' : trim($_SERVER['HTTP_REFERER']);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get the accepted languages\n\t *\n\t * @return\tarray\n\t */\n\tpublic function languages()\n\t{\n\t\tif (count($this->languages) === 0)\n\t\t{\n\t\t\t$this->_set_languages();\n\t\t}\n\n\t\treturn $this->languages;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get the accepted Character Sets\n\t *\n\t * @return\tarray\n\t */\n\tpublic function charsets()\n\t{\n\t\tif (count($this->charsets) === 0)\n\t\t{\n\t\t\t$this->_set_charsets();\n\t\t}\n\n\t\treturn $this->charsets;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Test for a particular language\n\t *\n\t * @param\tstring\t$lang\n\t * @return\tbool\n\t */\n\tpublic function accept_lang($lang = 'en')\n\t{\n\t\treturn in_array(strtolower($lang), $this->languages(), TRUE);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Test for a particular character set\n\t *\n\t * @param\tstring\t$charset\n\t * @return\tbool\n\t */\n\tpublic function accept_charset($charset = 'utf-8')\n\t{\n\t\treturn in_array(strtolower($charset), $this->charsets(), TRUE);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Parse a custom user-agent string\n\t *\n\t * @param\tstring\t$string\n\t * @return\tvoid\n\t */\n\tpublic function parse($string)\n\t{\n\t\t// Reset values\n\t\t$this->is_browser = FALSE;\n\t\t$this->is_robot = FALSE;\n\t\t$this->is_mobile = FALSE;\n\t\t$this->browser = '';\n\t\t$this->version = '';\n\t\t$this->mobile = '';\n\t\t$this->robot = '';\n\n\t\t// Set the new user-agent string and parse it, unless empty\n\t\t$this->agent = $string;\n\n\t\tif ( ! empty($string))\n\t\t{\n\t\t\t$this->_compile_data();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "system/libraries/Xmlrpc.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\nif ( ! function_exists('xml_parser_create'))\n{\n\tshow_error('Your PHP installation does not support XML');\n}\n\n// ------------------------------------------------------------------------\n\n/**\n * XML-RPC request handler class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tXML-RPC\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/xmlrpc.html\n */\nclass CI_Xmlrpc {\n\n\t/**\n\t * Debug flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $debug\t\t= FALSE;\n\n\t/**\n\t * I4 data type\n\t *\n\t * @var\tstring\n\t */\n\tpublic $xmlrpcI4\t= 'i4';\n\n\t/**\n\t * Integer data type\n\t *\n\t * @var\tstring\n\t */\n\tpublic $xmlrpcInt\t= 'int';\n\n\t/**\n\t * Boolean data type\n\t *\n\t * @var\tstring\n\t */\n\tpublic $xmlrpcBoolean\t= 'boolean';\n\n\t/**\n\t * Double data type\n\t *\n\t * @var\tstring\n\t */\n\tpublic $xmlrpcDouble\t= 'double';\n\n\t/**\n\t * String data type\n\t *\n\t * @var\tstring\n\t */\n\tpublic $xmlrpcString\t= 'string';\n\n\t/**\n\t * DateTime format\n\t *\n\t * @var\tstring\n\t */\n\tpublic $xmlrpcDateTime\t= 'dateTime.iso8601';\n\n\t/**\n\t * Base64 data type\n\t *\n\t * @var\tstring\n\t */\n\tpublic $xmlrpcBase64\t= 'base64';\n\n\t/**\n\t * Array data type\n\t *\n\t * @var\tstring\n\t */\n\tpublic $xmlrpcArray\t= 'array';\n\n\t/**\n\t * Struct data type\n\t *\n\t * @var\tstring\n\t */\n\tpublic $xmlrpcStruct\t= 'struct';\n\n\t/**\n\t * Data types list\n\t *\n\t * @var\tarray\n\t */\n\tpublic $xmlrpcTypes\t= array();\n\n\t/**\n\t * Valid parents list\n\t *\n\t * @var\tarray\n\t */\n\tpublic $valid_parents\t= array();\n\n\t/**\n\t * Response error numbers list\n\t *\n\t * @var\tarray\n\t */\n\tpublic $xmlrpcerr\t\t= array();\n\n\t/**\n\t * Response error messages list\n\t *\n\t * @var\tstring[]\n\t */\n\tpublic $xmlrpcstr\t\t= array();\n\n\t/**\n\t * Encoding charset\n\t *\n\t * @var\tstring\n\t */\n\tpublic $xmlrpc_defencoding\t= 'UTF-8';\n\n\t/**\n\t * XML-RPC client name\n\t *\n\t * @var\tstring\n\t */\n\tpublic $xmlrpcName\t\t= 'XML-RPC for CodeIgniter';\n\n\t/**\n\t * XML-RPC version\n\t *\n\t * @var\tstring\n\t */\n\tpublic $xmlrpcVersion\t\t= '1.1';\n\n\t/**\n\t * Start of user errors\n\t *\n\t * @var\tint\n\t */\n\tpublic $xmlrpcerruser\t\t= 800;\n\n\t/**\n\t * Start of XML parse errors\n\t *\n\t * @var\tint\n\t */\n\tpublic $xmlrpcerrxml\t\t= 100;\n\n\t/**\n\t * Backslash replacement value\n\t *\n\t * @var\tstring\n\t */\n\tpublic $xmlrpc_backslash\t= '';\n\n\t/**\n\t * XML-RPC Client object\n\t *\n\t * @var\tobject\n\t */\n\tpublic $client;\n\n\t/**\n\t * XML-RPC Method name\n\t *\n\t * @var\tstring\n\t */\n\tpublic $method;\n\n\t/**\n\t * XML-RPC Data\n\t *\n\t * @var\tarray\n\t */\n\tpublic $data;\n\n\t/**\n\t * XML-RPC Message\n\t *\n\t * @var\tstring\n\t */\n\tpublic $message\t\t\t= '';\n\n\t/**\n\t * Request error message\n\t *\n\t * @var\tstring\n\t */\n\tpublic $error\t\t\t= '';\n\n\t/**\n\t * XML-RPC result object\n\t *\n\t * @var\tobject\n\t */\n\tpublic $result;\n\n\t/**\n\t * XML-RPC Response\n\t *\n\t * @var\tarray\n\t */\n\tpublic $response\t\t= array(); // Response from remote server\n\n\t/**\n\t * XSS Filter flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $xss_clean\t\t= TRUE;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Constructor\n\t *\n\t * Initializes property default values\n\t *\n\t * @param\tarray\t$config\n\t * @return\tvoid\n\t */\n\tpublic function __construct($config = array())\n\t{\n\t\t$this->xmlrpc_backslash = chr(92).chr(92);\n\n\t\t// Types for info sent back and forth\n\t\t$this->xmlrpcTypes = array(\n\t\t\t$this->xmlrpcI4\t \t\t=> '1',\n\t\t\t$this->xmlrpcInt\t\t=> '1',\n\t\t\t$this->xmlrpcBoolean\t=> '1',\n\t\t\t$this->xmlrpcString\t\t=> '1',\n\t\t\t$this->xmlrpcDouble\t\t=> '1',\n\t\t\t$this->xmlrpcDateTime\t=> '1',\n\t\t\t$this->xmlrpcBase64\t\t=> '1',\n\t\t\t$this->xmlrpcArray\t\t=> '2',\n\t\t\t$this->xmlrpcStruct\t\t=> '3'\n\t\t);\n\n\t\t// Array of Valid Parents for Various XML-RPC elements\n\t\t$this->valid_parents = array('BOOLEAN' => array('VALUE'),\n\t\t\t'I4'\t\t\t\t=> array('VALUE'),\n\t\t\t'INT'\t\t\t\t=> array('VALUE'),\n\t\t\t'STRING'\t\t\t=> array('VALUE'),\n\t\t\t'DOUBLE'\t\t\t=> array('VALUE'),\n\t\t\t'DATETIME.ISO8601'\t=> array('VALUE'),\n\t\t\t'BASE64'\t\t\t=> array('VALUE'),\n\t\t\t'ARRAY'\t\t\t=> array('VALUE'),\n\t\t\t'STRUCT'\t\t\t=> array('VALUE'),\n\t\t\t'PARAM'\t\t\t=> array('PARAMS'),\n\t\t\t'METHODNAME'\t\t=> array('METHODCALL'),\n\t\t\t'PARAMS'\t\t\t=> array('METHODCALL', 'METHODRESPONSE'),\n\t\t\t'MEMBER'\t\t\t=> array('STRUCT'),\n\t\t\t'NAME'\t\t\t\t=> array('MEMBER'),\n\t\t\t'DATA'\t\t\t\t=> array('ARRAY'),\n\t\t\t'FAULT'\t\t\t=> array('METHODRESPONSE'),\n\t\t\t'VALUE'\t\t\t=> array('MEMBER', 'DATA', 'PARAM', 'FAULT')\n\t\t);\n\n\t\t// XML-RPC Responses\n\t\t$this->xmlrpcerr['unknown_method'] = '1';\n\t\t$this->xmlrpcstr['unknown_method'] = 'This is not a known method for this XML-RPC Server';\n\t\t$this->xmlrpcerr['invalid_return'] = '2';\n\t\t$this->xmlrpcstr['invalid_return'] = 'The XML data received was either invalid or not in the correct form for XML-RPC. Turn on debugging to examine the XML data further.';\n\t\t$this->xmlrpcerr['incorrect_params'] = '3';\n\t\t$this->xmlrpcstr['incorrect_params'] = 'Incorrect parameters were passed to method';\n\t\t$this->xmlrpcerr['introspect_unknown'] = '4';\n\t\t$this->xmlrpcstr['introspect_unknown'] = 'Cannot inspect signature for request: method unknown';\n\t\t$this->xmlrpcerr['http_error'] = '5';\n\t\t$this->xmlrpcstr['http_error'] = \"Did not receive a '200 OK' response from remote server.\";\n\t\t$this->xmlrpcerr['no_data'] = '6';\n\t\t$this->xmlrpcstr['no_data'] = 'No data received from server.';\n\n\t\t$this->initialize($config);\n\n\t\tlog_message('info', 'XML-RPC Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Initialize\n\t *\n\t * @param\tarray\t$config\n\t * @return\tvoid\n\t */\n\tpublic function initialize($config = array())\n\t{\n\t\tif (count($config) > 0)\n\t\t{\n\t\t\tforeach ($config as $key => $val)\n\t\t\t{\n\t\t\t\tif (isset($this->$key))\n\t\t\t\t{\n\t\t\t\t\t$this->$key = $val;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Parse server URL\n\t *\n\t * @param\tstring\t$url\n\t * @param\tint\t$port\n\t * @param\tstring\t$proxy\n\t * @param\tint\t$proxy_port\n\t * @return\tvoid\n\t */\n\tpublic function server($url, $port = 80, $proxy = FALSE, $proxy_port = 8080)\n\t{\n\t\tif (stripos($url, 'http') !== 0)\n\t\t{\n\t\t\t$url = 'http://'.$url;\n\t\t}\n\n\t\t$parts = parse_url($url);\n\n\t\tif (isset($parts['user'], $parts['pass']))\n\t\t{\n\t\t\t$parts['host'] = $parts['user'].':'.$parts['pass'].'@'.$parts['host'];\n\t\t}\n\n\t\t$path = isset($parts['path']) ? $parts['path'] : '/';\n\n\t\tif ( ! empty($parts['query']))\n\t\t{\n\t\t\t$path .= '?'.$parts['query'];\n\t\t}\n\n\t\t$this->client = new XML_RPC_Client($path, $parts['host'], $port, $proxy, $proxy_port);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Timeout\n\t *\n\t * @param\tint\t$seconds\n\t * @return\tvoid\n\t */\n\tpublic function timeout($seconds = 5)\n\t{\n\t\tif ($this->client !== NULL && is_int($seconds))\n\t\t{\n\t\t\t$this->client->timeout = $seconds;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Methods\n\t *\n\t * @param\tstring\t$function\tMethod name\n\t * @return\tvoid\n\t */\n\tpublic function method($function)\n\t{\n\t\t$this->method = $function;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Take Array of Data and Create Objects\n\t *\n\t * @param\tarray\t$incoming\n\t * @return\tvoid\n\t */\n\tpublic function request($incoming)\n\t{\n\t\tif ( ! is_array($incoming))\n\t\t{\n\t\t\t// Send Error\n\t\t\treturn;\n\t\t}\n\n\t\t$this->data = array();\n\n\t\tforeach ($incoming as $key => $value)\n\t\t{\n\t\t\t$this->data[$key] = $this->values_parsing($value);\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Set Debug\n\t *\n\t * @param\tbool\t$flag\n\t * @return\tvoid\n\t */\n\tpublic function set_debug($flag = TRUE)\n\t{\n\t\t$this->debug = ($flag === TRUE);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Values Parsing\n\t *\n\t * @param\tmixed\t$value\n\t * @return\tobject\n\t */\n\tpublic function values_parsing($value)\n\t{\n\t\tif (is_array($value) && array_key_exists(0, $value))\n\t\t{\n\t\t\tif ( ! isset($value[1], $this->xmlrpcTypes[$value[1]]))\n\t\t\t{\n\t\t\t\t$temp = new XML_RPC_Values($value[0], (is_array($value[0]) ? 'array' : 'string'));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (is_array($value[0]) && ($value[1] === 'struct' OR $value[1] === 'array'))\n\t\t\t\t{\n\t\t\t\t\tforeach (array_keys($value[0]) as $k)\n\t\t\t\t\t{\n\t\t\t\t\t\t$value[0][$k] = $this->values_parsing($value[0][$k]);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$temp = new XML_RPC_Values($value[0], $value[1]);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$temp = new XML_RPC_Values($value, 'string');\n\t\t}\n\n\t\treturn $temp;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Sends XML-RPC Request\n\t *\n\t * @return\tbool\n\t */\n\tpublic function send_request()\n\t{\n\t\t$this->message = new XML_RPC_Message($this->method, $this->data);\n\t\t$this->message->debug = $this->debug;\n\n\t\tif ( ! $this->result = $this->client->send($this->message) OR ! is_object($this->result->val))\n\t\t{\n\t\t\t$this->error = $this->result->errstr;\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t$this->response = $this->result->decode();\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns Error\n\t *\n\t * @return\tstring\n\t */\n\tpublic function display_error()\n\t{\n\t\treturn $this->error;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Returns Remote Server Response\n\t *\n\t * @return\tstring\n\t */\n\tpublic function display_response()\n\t{\n\t\treturn $this->response;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Sends an Error Message for Server Request\n\t *\n\t * @param\tint\t$number\n\t * @param\tstring\t$message\n\t * @return\tobject\n\t */\n\tpublic function send_error_message($number, $message)\n\t{\n\t\treturn new XML_RPC_Response(0, $number, $message);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Send Response for Server Request\n\t *\n\t * @param\tarray\t$response\n\t * @return\tobject\n\t */\n\tpublic function send_response($response)\n\t{\n\t\t// $response should be array of values, which will be parsed\n\t\t// based on their data and type into a valid group of XML-RPC values\n\t\treturn new XML_RPC_Response($this->values_parsing($response));\n\t}\n\n} // END XML_RPC Class\n\n/**\n * XML-RPC Client class\n *\n * @category\tXML-RPC\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/xmlrpc.html\n */\nclass XML_RPC_Client extends CI_Xmlrpc\n{\n\t/**\n\t * Path\n\t *\n\t * @var\tstring\n\t */\n\tpublic $path\t\t\t= '';\n\n\t/**\n\t * Server hostname\n\t *\n\t * @var\tstring\n\t */\n\tpublic $server\t\t\t= '';\n\n\t/**\n\t * Server port\n\t *\n\t * @var\tint\n\t */\n\tpublic $port\t\t\t= 80;\n\n\t/**\n\t *\n\t * Server username\n\t *\n\t * @var\tstring\n\t */\n\tpublic $username;\n\n\t/**\n\t * Server password\n\t *\n\t * @var\tstring\n\t */\n\tpublic $password;\n\n\t/**\n\t * Proxy hostname\n\t *\n\t * @var\tstring\n\t */\n\tpublic $proxy\t\t\t= FALSE;\n\n\t/**\n\t * Proxy port\n\t *\n\t * @var\tint\n\t */\n\tpublic $proxy_port\t\t= 8080;\n\n\t/**\n\t * Error number\n\t *\n\t * @var\tstring\n\t */\n\tpublic $errno\t\t\t= '';\n\n\t/**\n\t * Error message\n\t *\n\t * @var\tstring\n\t */\n\tpublic $errstring\t\t= '';\n\n\t/**\n\t * Timeout in seconds\n\t *\n\t * @var\tint\n\t */\n\tpublic $timeout\t\t= 5;\n\n\t/**\n\t * No Multicall flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $no_multicall\t= FALSE;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Constructor\n\t *\n\t * @param\tstring\t$path\n\t * @param\tobject\t$server\n\t * @param\tint\t$port\n\t * @param\tstring\t$proxy\n\t * @param\tint\t$proxy_port\n\t * @return\tvoid\n\t */\n\tpublic function __construct($path, $server, $port = 80, $proxy = FALSE, $proxy_port = 8080)\n\t{\n\t\tparent::__construct();\n\n\t\t$url = parse_url('http://'.$server);\n\n\t\tif (isset($url['user'], $url['pass']))\n\t\t{\n\t\t\t$this->username = $url['user'];\n\t\t\t$this->password = $url['pass'];\n\t\t}\n\n\t\t$this->port = $port;\n\t\t$this->server = $url['host'];\n\t\t$this->path = $path;\n\t\t$this->proxy = $proxy;\n\t\t$this->proxy_port = $proxy_port;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Send message\n\t *\n\t * @param\tmixed\t$msg\n\t * @return\tobject\n\t */\n\tpublic function send($msg)\n\t{\n\t\tif (is_array($msg))\n\t\t{\n\t\t\t// Multi-call disabled\n\t\t\treturn new XML_RPC_Response(0, $this->xmlrpcerr['multicall_recursion'], $this->xmlrpcstr['multicall_recursion']);\n\t\t}\n\n\t\treturn $this->sendPayload($msg);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Send payload\n\t *\n\t * @param\tobject\t$msg\n\t * @return\tobject\n\t */\n\tpublic function sendPayload($msg)\n\t{\n\t\tif ($this->proxy === FALSE)\n\t\t{\n\t\t\t$server = $this->server;\n\t\t\t$port = $this->port;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$server = $this->proxy;\n\t\t\t$port = $this->proxy_port;\n\t\t}\n\n\t\t$fp = @fsockopen($server, $port, $this->errno, $this->errstring, $this->timeout);\n\n\t\tif ( ! is_resource($fp))\n\t\t{\n\t\t\terror_log($this->xmlrpcstr['http_error']);\n\t\t\treturn new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']);\n\t\t}\n\n\t\tif (empty($msg->payload))\n\t\t{\n\t\t\t// $msg = XML_RPC_Messages\n\t\t\t$msg->createPayload();\n\t\t}\n\n\t\t$r = \"\\r\\n\";\n\t\t$op = 'POST '.$this->path.' HTTP/1.0'.$r\n\t\t\t.'Host: '.$this->server.$r\n\t\t\t.'Content-Type: text/xml'.$r\n\t\t\t.(isset($this->username, $this->password) ? 'Authorization: Basic '.base64_encode($this->username.':'.$this->password).$r : '')\n\t\t\t.'User-Agent: '.$this->xmlrpcName.$r\n\t\t\t.'Content-Length: '.strlen($msg->payload).$r.$r\n\t\t\t.$msg->payload;\n\n\t\tstream_set_timeout($fp, $this->timeout); // set timeout for subsequent operations\n\n\t\tfor ($written = $timestamp = 0, $length = strlen($op); $written < $length; $written += $result)\n\t\t{\n\t\t\tif (($result = fwrite($fp, substr($op, $written))) === FALSE)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t// See https://bugs.php.net/bug.php?id=39598 and https://secure.php.net/manual/en/function.fwrite.php#96951\n\t\t\telseif ($result === 0)\n\t\t\t{\n\t\t\t\tif ($timestamp === 0)\n\t\t\t\t{\n\t\t\t\t\t$timestamp = time();\n\t\t\t\t}\n\t\t\t\telseif ($timestamp < (time() - $this->timeout))\n\t\t\t\t{\n\t\t\t\t\t$result = FALSE;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$timestamp = 0;\n\t\t\t}\n\t\t}\n\n\t\tif ($result === FALSE)\n\t\t{\n\t\t\terror_log($this->xmlrpcstr['http_error']);\n\t\t\treturn new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']);\n\t\t}\n\n\t\t$resp = $msg->parseResponse($fp);\n\t\tfclose($fp);\n\t\treturn $resp;\n\t}\n\n} // END XML_RPC_Client Class\n\n/**\n * XML-RPC Response class\n *\n * @category\tXML-RPC\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/xmlrpc.html\n */\nclass XML_RPC_Response\n{\n\n\t/**\n\t * Value\n\t *\n\t * @var\tmixed\n\t */\n\tpublic $val\t\t= 0;\n\n\t/**\n\t * Error number\n\t *\n\t * @var\tint\n\t */\n\tpublic $errno\t\t= 0;\n\n\t/**\n\t * Error message\n\t *\n\t * @var\tstring\n\t */\n\tpublic $errstr\t\t= '';\n\n\t/**\n\t * Headers list\n\t *\n\t * @var\tarray\n\t */\n\tpublic $headers\t\t= array();\n\n\t/**\n\t * XSS Filter flag\n\t *\n\t * @var\tbool\n\t */\n\tpublic $xss_clean\t= TRUE;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Constructor\n\t *\n\t * @param\tmixed\t$val\n\t * @param\tint\t$code\n\t * @param\tstring\t$fstr\n\t * @return\tvoid\n\t */\n\tpublic function __construct($val, $code = 0, $fstr = '')\n\t{\n\t\tif ($code !== 0)\n\t\t{\n\t\t\t// error\n\t\t\t$this->errno = $code;\n\t\t\t$this->errstr = htmlspecialchars($fstr, ENT_XML1 | ENT_NOQUOTES, 'UTF-8');\n\t\t}\n\t\telseif ( ! is_object($val))\n\t\t{\n\t\t\t// programmer error, not an object\n\t\t\terror_log(\"Invalid type '\".gettype($val).\"' (value: \".$val.') passed to XML_RPC_Response. Defaulting to empty value.');\n\t\t\t$this->val = new XML_RPC_Values();\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$this->val = $val;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fault code\n\t *\n\t * @return\tint\n\t */\n\tpublic function faultCode()\n\t{\n\t\treturn $this->errno;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Fault string\n\t *\n\t * @return\tstring\n\t */\n\tpublic function faultString()\n\t{\n\t\treturn $this->errstr;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Value\n\t *\n\t * @return\tmixed\n\t */\n\tpublic function value()\n\t{\n\t\treturn $this->val;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Prepare response\n\t *\n\t * @return\tstring\txml\n\t */\n\tpublic function prepare_response()\n\t{\n\t\treturn \"<methodResponse>\\n\"\n\t\t\t.($this->errno\n\t\t\t\t? '<fault>\n\t<value>\n\t\t<struct>\n\t\t\t<member>\n\t\t\t\t<name>faultCode</name>\n\t\t\t\t<value><int>'.$this->errno.'</int></value>\n\t\t\t</member>\n\t\t\t<member>\n\t\t\t\t<name>faultString</name>\n\t\t\t\t<value><string>'.$this->errstr.'</string></value>\n\t\t\t</member>\n\t\t</struct>\n\t</value>\n</fault>'\n\t\t\t\t: \"<params>\\n<param>\\n\".$this->val->serialize_class().\"</param>\\n</params>\")\n\t\t\t.\"\\n</methodResponse>\";\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Decode\n\t *\n\t * @param\tmixed\t$array\n\t * @return\tarray\n\t */\n\tpublic function decode($array = NULL)\n\t{\n\t\t$CI =& get_instance();\n\n\t\tif (is_array($array))\n\t\t{\n\t\t\tforeach ($array as $key => &$value)\n\t\t\t{\n\t\t\t\tif (is_array($value))\n\t\t\t\t{\n\t\t\t\t\t$array[$key] = $this->decode($value);\n\t\t\t\t}\n\t\t\t\telseif ($this->xss_clean)\n\t\t\t\t{\n\t\t\t\t\t$array[$key] = $CI->security->xss_clean($value);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn $array;\n\t\t}\n\n\t\t$result = $this->xmlrpc_decoder($this->val);\n\n\t\tif (is_array($result))\n\t\t{\n\t\t\t$result = $this->decode($result);\n\t\t}\n\t\telseif ($this->xss_clean)\n\t\t{\n\t\t\t$result = $CI->security->xss_clean($result);\n\t\t}\n\n\t\treturn $result;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * XML-RPC Object to PHP Types\n\t *\n\t * @param\tobject\n\t * @return\tarray\n\t */\n\tpublic function xmlrpc_decoder($xmlrpc_val)\n\t{\n\t\t$kind = $xmlrpc_val->kindOf();\n\n\t\tif ($kind === 'scalar')\n\t\t{\n\t\t\treturn $xmlrpc_val->scalarval();\n\t\t}\n\t\telseif ($kind === 'array')\n\t\t{\n\t\t\treset($xmlrpc_val->me);\n\t\t\t$b = current($xmlrpc_val->me);\n\t\t\t$arr = array();\n\n\t\t\tfor ($i = 0, $size = count($b); $i < $size; $i++)\n\t\t\t{\n\t\t\t\t$arr[] = $this->xmlrpc_decoder($xmlrpc_val->me['array'][$i]);\n\t\t\t}\n\t\t\treturn $arr;\n\t\t}\n\t\telseif ($kind === 'struct')\n\t\t{\n\t\t\treset($xmlrpc_val->me['struct']);\n\t\t\t$arr = array();\n\n\t\t\tforeach ($xmlrpc_val->me['struct'] as $key => &$value)\n\t\t\t{\n\t\t\t\t$arr[$key] = $this->xmlrpc_decoder($value);\n\t\t\t}\n\n\t\t\treturn $arr;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * ISO-8601 time to server or UTC time\n\t *\n\t * @param\tstring\n\t * @param\tbool\n\t * @return\tint\tunix timestamp\n\t */\n\tpublic function iso8601_decode($time, $utc = FALSE)\n\t{\n\t\t// Return a time in the localtime, or UTC\n\t\t$t = 0;\n\t\tif (preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $time, $regs))\n\t\t{\n\t\t\t$fnc = ($utc === TRUE) ? 'gmmktime' : 'mktime';\n\t\t\t$t = $fnc($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);\n\t\t}\n\t\treturn $t;\n\t}\n\n} // END XML_RPC_Response Class\n\n/**\n * XML-RPC Message class\n *\n * @category\tXML-RPC\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/xmlrpc.html\n */\nclass XML_RPC_Message extends CI_Xmlrpc\n{\n\n\t/**\n\t * Payload\n\t *\n\t * @var\tstring\n\t */\n\tpublic $payload;\n\n\t/**\n\t * Method name\n\t *\n\t * @var\tstring\n\t */\n\tpublic $method_name;\n\n\t/**\n\t * Parameter list\n\t *\n\t * @var\tarray\n\t */\n\tpublic $params\t\t= array();\n\n\t/**\n\t * XH?\n\t *\n\t * @var\tarray\n\t */\n\tpublic $xh\t\t= array();\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Constructor\n\t *\n\t * @param\tstring\t$method\n\t * @param\tarray\t$pars\n\t * @return\tvoid\n\t */\n\tpublic function __construct($method, $pars = FALSE)\n\t{\n\t\tparent::__construct();\n\n\t\t$this->method_name = $method;\n\t\tif (is_array($pars) && count($pars) > 0)\n\t\t{\n\t\t\tfor ($i = 0, $c = count($pars); $i < $c; $i++)\n\t\t\t{\n\t\t\t\t// $pars[$i] = XML_RPC_Values\n\t\t\t\t$this->params[] = $pars[$i];\n\t\t\t}\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Create Payload to Send\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function createPayload()\n\t{\n\t\t$this->payload = '<?xml version=\"1.0\"?'.\">\\r\\n<methodCall>\\r\\n\"\n\t\t\t\t.'<methodName>'.$this->method_name.\"</methodName>\\r\\n\"\n\t\t\t\t.\"<params>\\r\\n\";\n\n\t\tfor ($i = 0, $c = count($this->params); $i < $c; $i++)\n\t\t{\n\t\t\t// $p = XML_RPC_Values\n\t\t\t$p = $this->params[$i];\n\t\t\t$this->payload .= \"<param>\\r\\n\".$p->serialize_class().\"</param>\\r\\n\";\n\t\t}\n\n\t\t$this->payload .= \"</params>\\r\\n</methodCall>\\r\\n\";\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Parse External XML-RPC Server's Response\n\t *\n\t * @param\tresource\n\t * @return\tobject\n\t */\n\tpublic function parseResponse($fp)\n\t{\n\t\t$data = '';\n\n\t\twhile ($datum = fread($fp, 4096))\n\t\t{\n\t\t\t$data .= $datum;\n\t\t}\n\n\t\t// Display HTTP content for debugging\n\t\tif ($this->debug === TRUE)\n\t\t{\n\t\t\techo \"<pre>---DATA---\\n\".htmlspecialchars($data).\"\\n---END DATA---\\n\\n</pre>\";\n\t\t}\n\n\t\t// Check for data\n\t\tif ($data === '')\n\t\t{\n\t\t\terror_log($this->xmlrpcstr['no_data']);\n\t\t\treturn new XML_RPC_Response(0, $this->xmlrpcerr['no_data'], $this->xmlrpcstr['no_data']);\n\t\t}\n\n\t\t// Check for HTTP 200 Response\n\t\tif (strpos($data, 'HTTP') === 0 && ! preg_match('/^HTTP\\/[0-9\\.]+ 200 /', $data))\n\t\t{\n\t\t\t$errstr = substr($data, 0, strpos($data, \"\\n\")-1);\n\t\t\treturn new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error'].' ('.$errstr.')');\n\t\t}\n\n\t\t//-------------------------------------\n\t\t// Create and Set Up XML Parser\n\t\t//-------------------------------------\n\n\t\t$parser = xml_parser_create($this->xmlrpc_defencoding);\n\t\t$pname = (string) $parser;\n\t\t$this->xh[$pname] = array(\n\t\t\t'isf'\t\t=> 0,\n\t\t\t'ac'\t\t=> '',\n\t\t\t'headers'\t=> array(),\n\t\t\t'stack'\t\t=> array(),\n\t\t\t'valuestack'\t=> array(),\n\t\t\t'isf_reason'\t=> 0\n\t\t);\n\n\t\txml_set_object($parser, $this);\n\t\txml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, TRUE);\n\t\txml_set_element_handler($parser, 'open_tag', 'closing_tag');\n\t\txml_set_character_data_handler($parser, 'character_data');\n\t\t//xml_set_default_handler($parser, 'default_handler');\n\n\t\t// Get headers\n\t\t$lines = explode(\"\\r\\n\", $data);\n\t\twhile (($line = array_shift($lines)))\n\t\t{\n\t\t\tif (strlen($line) < 1)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t$this->xh[$pname]['headers'][] = $line;\n\t\t}\n\t\t$data = implode(\"\\r\\n\", $lines);\n\n\t\t// Parse XML data\n\t\tif ( ! xml_parse($parser, $data, TRUE))\n\t\t{\n\t\t\t$errstr = sprintf('XML error: %s at line %d',\n\t\t\t\t\t\txml_error_string(xml_get_error_code($parser)),\n\t\t\t\t\t\txml_get_current_line_number($parser));\n\n\t\t\t$r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']);\n\t\t\txml_parser_free($parser);\n\t\t\treturn $r;\n\t\t}\n\t\txml_parser_free($parser);\n\n\t\t// Got ourselves some badness, it seems\n\t\tif ($this->xh[$pname]['isf'] > 1)\n\t\t{\n\t\t\tif ($this->debug === TRUE)\n\t\t\t{\n\t\t\t\techo \"---Invalid Return---\\n\".$this->xh[$pname]['isf_reason'].\"---Invalid Return---\\n\\n\";\n\t\t\t}\n\n\t\t\treturn new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return'].' '.$this->xh[$pname]['isf_reason']);\n\t\t}\n\t\telseif ( ! is_object($this->xh[$pname]['value']))\n\t\t{\n\t\t\treturn new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return'].' '.$this->xh[$pname]['isf_reason']);\n\t\t}\n\n\t\t// Display XML content for debugging\n\t\tif ($this->debug === TRUE)\n\t\t{\n\t\t\techo '<pre>';\n\n\t\t\tif (count($this->xh[$pname]['headers']) > 0)\n\t\t\t{\n\t\t\t\techo \"---HEADERS---\\n\";\n\t\t\t\tforeach ($this->xh[$pname]['headers'] as $header)\n\t\t\t\t{\n\t\t\t\t\techo $header.\"\\n\";\n\t\t\t\t}\n\t\t\t\techo \"---END HEADERS---\\n\\n\";\n\t\t\t}\n\n\t\t\techo \"---DATA---\\n\".htmlspecialchars($data).\"\\n---END DATA---\\n\\n---PARSED---\\n\";\n\t\t\tvar_dump($this->xh[$pname]['value']);\n\t\t\techo \"\\n---END PARSED---</pre>\";\n\t\t}\n\n\t\t// Send response\n\t\t$v = $this->xh[$pname]['value'];\n\t\tif ($this->xh[$pname]['isf'])\n\t\t{\n\t\t\t$errno_v = $v->me['struct']['faultCode'];\n\t\t\t$errstr_v = $v->me['struct']['faultString'];\n\t\t\t$errno = $errno_v->scalarval();\n\n\t\t\tif ($errno === 0)\n\t\t\t{\n\t\t\t\t// FAULT returned, errno needs to reflect that\n\t\t\t\t$errno = -1;\n\t\t\t}\n\n\t\t\t$r = new XML_RPC_Response($v, $errno, $errstr_v->scalarval());\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$r = new XML_RPC_Response($v);\n\t\t}\n\n\t\t$r->headers = $this->xh[$pname]['headers'];\n\t\treturn $r;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t// ------------------------------------\n\t//  Begin Return Message Parsing section\n\t// ------------------------------------\n\n\t// quick explanation of components:\n\t//   ac - used to accumulate values\n\t//   isf - used to indicate a fault\n\t//   lv - used to indicate \"looking for a value\": implements\n\t//\t\tthe logic to allow values with no types to be strings\n\t//   params - used to store parameters in method calls\n\t//   method - used to store method name\n\t//\t stack - array with parent tree of the xml element,\n\t//\t\t\t used to validate the nesting of elements\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Start Element Handler\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tvoid\n\t */\n\tpublic function open_tag($the_parser, $name)\n\t{\n\t\t$the_parser = (string) $the_parser;\n\n\t\t// If invalid nesting, then return\n\t\tif ($this->xh[$the_parser]['isf'] > 1) return;\n\n\t\t// Evaluate and check for correct nesting of XML elements\n\t\tif (count($this->xh[$the_parser]['stack']) === 0)\n\t\t{\n\t\t\tif ($name !== 'METHODRESPONSE' && $name !== 'METHODCALL')\n\t\t\t{\n\t\t\t\t$this->xh[$the_parser]['isf'] = 2;\n\t\t\t\t$this->xh[$the_parser]['isf_reason'] = 'Top level XML-RPC element is missing';\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t// not top level element: see if parent is OK\n\t\telseif ( ! in_array($this->xh[$the_parser]['stack'][0], $this->valid_parents[$name], TRUE))\n\t\t{\n\t\t\t$this->xh[$the_parser]['isf'] = 2;\n\t\t\t$this->xh[$the_parser]['isf_reason'] = 'XML-RPC element '.$name.' cannot be child of '.$this->xh[$the_parser]['stack'][0];\n\t\t\treturn;\n\t\t}\n\n\t\tswitch ($name)\n\t\t{\n\t\t\tcase 'STRUCT':\n\t\t\tcase 'ARRAY':\n\t\t\t\t// Creates array for child elements\n\t\t\t\t$cur_val = array('value' => array(), 'type' => $name);\n\t\t\t\tarray_unshift($this->xh[$the_parser]['valuestack'], $cur_val);\n\t\t\t\tbreak;\n\t\t\tcase 'METHODNAME':\n\t\t\tcase 'NAME':\n\t\t\t\t$this->xh[$the_parser]['ac'] = '';\n\t\t\t\tbreak;\n\t\t\tcase 'FAULT':\n\t\t\t\t$this->xh[$the_parser]['isf'] = 1;\n\t\t\t\tbreak;\n\t\t\tcase 'PARAM':\n\t\t\t\t$this->xh[$the_parser]['value'] = NULL;\n\t\t\t\tbreak;\n\t\t\tcase 'VALUE':\n\t\t\t\t$this->xh[$the_parser]['vt'] = 'value';\n\t\t\t\t$this->xh[$the_parser]['ac'] = '';\n\t\t\t\t$this->xh[$the_parser]['lv'] = 1;\n\t\t\t\tbreak;\n\t\t\tcase 'I4':\n\t\t\tcase 'INT':\n\t\t\tcase 'STRING':\n\t\t\tcase 'BOOLEAN':\n\t\t\tcase 'DOUBLE':\n\t\t\tcase 'DATETIME.ISO8601':\n\t\t\tcase 'BASE64':\n\t\t\t\tif ($this->xh[$the_parser]['vt'] !== 'value')\n\t\t\t\t{\n\t\t\t\t\t//two data elements inside a value: an error occurred!\n\t\t\t\t\t$this->xh[$the_parser]['isf'] = 2;\n\t\t\t\t\t$this->xh[$the_parser]['isf_reason'] = 'There is a '.$name.' element following a '\n\t\t\t\t\t\t\t\t\t\t.$this->xh[$the_parser]['vt'].' element inside a single value';\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t$this->xh[$the_parser]['ac'] = '';\n\t\t\t\tbreak;\n\t\t\tcase 'MEMBER':\n\t\t\t\t// Set name of <member> to nothing to prevent errors later if no <name> is found\n\t\t\t\t$this->xh[$the_parser]['valuestack'][0]['name'] = '';\n\n\t\t\t\t// Set NULL value to check to see if value passed for this param/member\n\t\t\t\t$this->xh[$the_parser]['value'] = NULL;\n\t\t\t\tbreak;\n\t\t\tcase 'DATA':\n\t\t\tcase 'METHODCALL':\n\t\t\tcase 'METHODRESPONSE':\n\t\t\tcase 'PARAMS':\n\t\t\t\t// valid elements that add little to processing\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t/// An Invalid Element is Found, so we have trouble\n\t\t\t\t$this->xh[$the_parser]['isf'] = 2;\n\t\t\t\t$this->xh[$the_parser]['isf_reason'] = 'Invalid XML-RPC element found: '.$name;\n\t\t\t\tbreak;\n\t\t}\n\n\t\t// Add current element name to stack, to allow validation of nesting\n\t\tarray_unshift($this->xh[$the_parser]['stack'], $name);\n\n\t\t$name === 'VALUE' OR $this->xh[$the_parser]['lv'] = 0;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * End Element Handler\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tvoid\n\t */\n\tpublic function closing_tag($the_parser, $name)\n\t{\n\t\t$the_parser = (string) $the_parser;\n\n\t\tif ($this->xh[$the_parser]['isf'] > 1) return;\n\n\t\t// Remove current element from stack and set variable\n\t\t// NOTE: If the XML validates, then we do not have to worry about\n\t\t// the opening and closing of elements. Nesting is checked on the opening\n\t\t// tag so we be safe there as well.\n\n\t\t$curr_elem = array_shift($this->xh[$the_parser]['stack']);\n\n\t\tswitch ($name)\n\t\t{\n\t\t\tcase 'STRUCT':\n\t\t\tcase 'ARRAY':\n\t\t\t\t$cur_val = array_shift($this->xh[$the_parser]['valuestack']);\n\t\t\t\t$this->xh[$the_parser]['value'] = isset($cur_val['values']) ? $cur_val['values'] : array();\n\t\t\t\t$this->xh[$the_parser]['vt']\t= strtolower($name);\n\t\t\t\tbreak;\n\t\t\tcase 'NAME':\n\t\t\t\t$this->xh[$the_parser]['valuestack'][0]['name'] = $this->xh[$the_parser]['ac'];\n\t\t\t\tbreak;\n\t\t\tcase 'BOOLEAN':\n\t\t\tcase 'I4':\n\t\t\tcase 'INT':\n\t\t\tcase 'STRING':\n\t\t\tcase 'DOUBLE':\n\t\t\tcase 'DATETIME.ISO8601':\n\t\t\tcase 'BASE64':\n\t\t\t\t$this->xh[$the_parser]['vt'] = strtolower($name);\n\n\t\t\t\tif ($name === 'STRING')\n\t\t\t\t{\n\t\t\t\t\t$this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];\n\t\t\t\t}\n\t\t\t\telseif ($name === 'DATETIME.ISO8601')\n\t\t\t\t{\n\t\t\t\t\t$this->xh[$the_parser]['vt']\t= $this->xmlrpcDateTime;\n\t\t\t\t\t$this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];\n\t\t\t\t}\n\t\t\t\telseif ($name === 'BASE64')\n\t\t\t\t{\n\t\t\t\t\t$this->xh[$the_parser]['value'] = base64_decode($this->xh[$the_parser]['ac']);\n\t\t\t\t}\n\t\t\t\telseif ($name === 'BOOLEAN')\n\t\t\t\t{\n\t\t\t\t\t// Translated BOOLEAN values to TRUE AND FALSE\n\t\t\t\t\t$this->xh[$the_parser]['value'] = (bool) $this->xh[$the_parser]['ac'];\n\t\t\t\t}\n\t\t\t\telseif ($name=='DOUBLE')\n\t\t\t\t{\n\t\t\t\t\t// we have a DOUBLE\n\t\t\t\t\t// we must check that only 0123456789-.<space> are characters here\n\t\t\t\t\t$this->xh[$the_parser]['value'] = preg_match('/^[+-]?[eE0-9\\t \\.]+$/', $this->xh[$the_parser]['ac'])\n\t\t\t\t\t\t\t\t\t\t? (float) $this->xh[$the_parser]['ac']\n\t\t\t\t\t\t\t\t\t\t: 'ERROR_NON_NUMERIC_FOUND';\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// we have an I4/INT\n\t\t\t\t\t// we must check that only 0123456789-<space> are characters here\n\t\t\t\t\t$this->xh[$the_parser]['value'] = preg_match('/^[+-]?[0-9\\t ]+$/', $this->xh[$the_parser]['ac'])\n\t\t\t\t\t\t\t\t\t\t? (int) $this->xh[$the_parser]['ac']\n\t\t\t\t\t\t\t\t\t\t: 'ERROR_NON_NUMERIC_FOUND';\n\t\t\t\t}\n\t\t\t\t$this->xh[$the_parser]['ac'] = '';\n\t\t\t\t$this->xh[$the_parser]['lv'] = 3; // indicate we've found a value\n\t\t\t\tbreak;\n\t\t\tcase 'VALUE':\n\t\t\t\t// This if() detects if no scalar was inside <VALUE></VALUE>\n\t\t\t\tif ($this->xh[$the_parser]['vt'] == 'value')\n\t\t\t\t{\n\t\t\t\t\t$this->xh[$the_parser]['value']\t= $this->xh[$the_parser]['ac'];\n\t\t\t\t\t$this->xh[$the_parser]['vt']\t= $this->xmlrpcString;\n\t\t\t\t}\n\n\t\t\t\t// build the XML-RPC value out of the data received, and substitute it\n\t\t\t\t$temp = new XML_RPC_Values($this->xh[$the_parser]['value'], $this->xh[$the_parser]['vt']);\n\n\t\t\t\tif (count($this->xh[$the_parser]['valuestack']) && $this->xh[$the_parser]['valuestack'][0]['type'] === 'ARRAY')\n\t\t\t\t{\n\t\t\t\t\t// Array\n\t\t\t\t\t$this->xh[$the_parser]['valuestack'][0]['values'][] = $temp;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// Struct\n\t\t\t\t\t$this->xh[$the_parser]['value'] = $temp;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'MEMBER':\n\t\t\t\t$this->xh[$the_parser]['ac'] = '';\n\n\t\t\t\t// If value add to array in the stack for the last element built\n\t\t\t\tif ($this->xh[$the_parser]['value'])\n\t\t\t\t{\n\t\t\t\t\t$this->xh[$the_parser]['valuestack'][0]['values'][$this->xh[$the_parser]['valuestack'][0]['name']] = $this->xh[$the_parser]['value'];\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'DATA':\n\t\t\t\t$this->xh[$the_parser]['ac'] = '';\n\t\t\t\tbreak;\n\t\t\tcase 'PARAM':\n\t\t\t\tif ($this->xh[$the_parser]['value'])\n\t\t\t\t{\n\t\t\t\t\t$this->xh[$the_parser]['params'][] = $this->xh[$the_parser]['value'];\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'METHODNAME':\n\t\t\t\t$this->xh[$the_parser]['method'] = ltrim($this->xh[$the_parser]['ac']);\n\t\t\t\tbreak;\n\t\t\tcase 'PARAMS':\n\t\t\tcase 'FAULT':\n\t\t\tcase 'METHODCALL':\n\t\t\tcase 'METHORESPONSE':\n\t\t\t\t// We're all good kids with nuthin' to do\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t// End of an Invalid Element. Taken care of during the opening tag though\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Parse character data\n\t *\n\t * @param\tstring\n\t * @param\tstring\n\t * @return\tvoid\n\t */\n\tpublic function character_data($the_parser, $data)\n\t{\n\t\t$the_parser = (string) $the_parser;\n\n\t\tif ($this->xh[$the_parser]['isf'] > 1) return; // XML Fault found already\n\n\t\t// If a value has not been found\n\t\tif ($this->xh[$the_parser]['lv'] !== 3)\n\t\t{\n\t\t\tif ($this->xh[$the_parser]['lv'] === 1)\n\t\t\t{\n\t\t\t\t$this->xh[$the_parser]['lv'] = 2; // Found a value\n\t\t\t}\n\n\t\t\tif ( ! isset($this->xh[$the_parser]['ac']))\n\t\t\t{\n\t\t\t\t$this->xh[$the_parser]['ac'] = '';\n\t\t\t}\n\n\t\t\t$this->xh[$the_parser]['ac'] .= $data;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Add parameter\n\t *\n\t * @param\tmixed\n\t * @return\tvoid\n\t */\n\tpublic function addParam($par)\n\t{\n\t\t$this->params[] = $par;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Output parameters\n\t *\n\t * @param\tarray\t$array\n\t * @return\tarray\n\t */\n\tpublic function output_parameters(array $array = array())\n\t{\n\t\t$CI =& get_instance();\n\n\t\tif ( ! empty($array))\n\t\t{\n\t\t\tforeach ($array as $key => &$value)\n\t\t\t{\n\t\t\t\tif (is_array($value))\n\t\t\t\t{\n\t\t\t\t\t$array[$key] = $this->output_parameters($value);\n\t\t\t\t}\n\t\t\t\telseif ($key !== 'bits' && $this->xss_clean)\n\t\t\t\t{\n\t\t\t\t\t// 'bits' is for the MetaWeblog API image bits\n\t\t\t\t\t// @todo - this needs to be made more general purpose\n\t\t\t\t\t$array[$key] = $CI->security->xss_clean($value);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn $array;\n\t\t}\n\n\t\t$parameters = array();\n\n\t\tfor ($i = 0, $c = count($this->params); $i < $c; $i++)\n\t\t{\n\t\t\t$a_param = $this->decode_message($this->params[$i]);\n\n\t\t\tif (is_array($a_param))\n\t\t\t{\n\t\t\t\t$parameters[] = $this->output_parameters($a_param);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$parameters[] = ($this->xss_clean) ? $CI->security->xss_clean($a_param) : $a_param;\n\t\t\t}\n\t\t}\n\n\t\treturn $parameters;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Decode message\n\t *\n\t * @param\tobject\n\t * @return\tmixed\n\t */\n\tpublic function decode_message($param)\n\t{\n\t\t$kind = $param->kindOf();\n\n\t\tif ($kind === 'scalar')\n\t\t{\n\t\t\treturn $param->scalarval();\n\t\t}\n\t\telseif ($kind === 'array')\n\t\t{\n\t\t\treset($param->me);\n\t\t\t$b = current($param->me);\n\t\t\t$arr = array();\n\n\t\t\tfor ($i = 0, $c = count($b); $i < $c; $i++)\n\t\t\t{\n\t\t\t\t$arr[] = $this->decode_message($param->me['array'][$i]);\n\t\t\t}\n\n\t\t\treturn $arr;\n\t\t}\n\t\telseif ($kind === 'struct')\n\t\t{\n\t\t\treset($param->me['struct']);\n\t\t\t$arr = array();\n\n\t\t\tforeach ($param->me['struct'] as $key => &$value)\n\t\t\t{\n\t\t\t\t$arr[$key] = $this->decode_message($value);\n\t\t\t}\n\n\t\t\treturn $arr;\n\t\t}\n\t}\n\n} // END XML_RPC_Message Class\n\n/**\n * XML-RPC Values class\n *\n * @category\tXML-RPC\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/xmlrpc.html\n */\nclass XML_RPC_Values extends CI_Xmlrpc\n{\n\t/**\n\t * Value data\n\t *\n\t * @var\tarray\n\t */\n\tpublic $me\t= array();\n\n\t/**\n\t * Value type\n\t *\n\t * @var\tint\n\t */\n\tpublic $mytype\t= 0;\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Constructor\n\t *\n\t * @param\tmixed\t$val\n\t * @param\tstring\t$type\n\t * @return\tvoid\n\t */\n\tpublic function __construct($val = -1, $type = '')\n\t{\n\t\tparent::__construct();\n\n\t\tif ($val !== -1 OR $type !== '')\n\t\t{\n\t\t\t$type = $type === '' ? 'string' : $type;\n\n\t\t\tif ($this->xmlrpcTypes[$type] == 1)\n\t\t\t{\n\t\t\t\t$this->addScalar($val, $type);\n\t\t\t}\n\t\t\telseif ($this->xmlrpcTypes[$type] == 2)\n\t\t\t{\n\t\t\t\t$this->addArray($val);\n\t\t\t}\n\t\t\telseif ($this->xmlrpcTypes[$type] == 3)\n\t\t\t{\n\t\t\t\t$this->addStruct($val);\n\t\t\t}\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Add scalar value\n\t *\n\t * @param\tscalar\n\t * @param\tstring\n\t * @return\tint\n\t */\n\tpublic function addScalar($val, $type = 'string')\n\t{\n\t\t$typeof = $this->xmlrpcTypes[$type];\n\n\t\tif ($this->mytype === 1)\n\t\t{\n\t\t\techo '<strong>XML_RPC_Values</strong>: scalar can have only one value<br />';\n\t\t\treturn 0;\n\t\t}\n\n\t\tif ($typeof != 1)\n\t\t{\n\t\t\techo \"<strong>XML_RPC_Values</strong>: not a scalar type ($typeof)<br />\";\n\t\t\treturn 0;\n\t\t}\n\n\t\tif ($type === $this->xmlrpcBoolean)\n\t\t{\n\t\t\t$val = (int) (strcasecmp($val, 'true') === 0 OR $val === 1 OR ($val === TRUE && strcasecmp($val, 'false')));\n\t\t}\n\n\t\tif ($this->mytype === 2)\n\t\t{\n\t\t\t// adding to an array here\n\t\t\t$ar = $this->me['array'];\n\t\t\t$ar[] = new XML_RPC_Values($val, $type);\n\t\t\t$this->me['array'] = $ar;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// a scalar, so set the value and remember we're scalar\n\t\t\t$this->me[$type] = $val;\n\t\t\t$this->mytype = $typeof;\n\t\t}\n\n\t\treturn 1;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Add array value\n\t *\n\t * @param\tarray\n\t * @return\tint\n\t */\n\tpublic function addArray($vals)\n\t{\n\t\tif ($this->mytype !== 0)\n\t\t{\n\t\t\techo '<strong>XML_RPC_Values</strong>: already initialized as a ['.$this->kindOf().']<br />';\n\t\t\treturn 0;\n\t\t}\n\n\t\t$this->mytype = $this->xmlrpcTypes['array'];\n\t\t$this->me['array'] = $vals;\n\t\treturn 1;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Add struct value\n\t *\n\t * @param\tobject\n\t * @return\tint\n\t */\n\tpublic function addStruct($vals)\n\t{\n\t\tif ($this->mytype !== 0)\n\t\t{\n\t\t\techo '<strong>XML_RPC_Values</strong>: already initialized as a ['.$this->kindOf().']<br />';\n\t\t\treturn 0;\n\t\t}\n\t\t$this->mytype = $this->xmlrpcTypes['struct'];\n\t\t$this->me['struct'] = $vals;\n\t\treturn 1;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get value type\n\t *\n\t * @return\tstring\n\t */\n\tpublic function kindOf()\n\t{\n\t\tswitch ($this->mytype)\n\t\t{\n\t\t\tcase 3: return 'struct';\n\t\t\tcase 2: return 'array';\n\t\t\tcase 1: return 'scalar';\n\t\t\tdefault: return 'undef';\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Serialize data\n\t *\n\t * @param\tstring\n\t * @param\tmixed\n\t * @return\tstring\n\t */\n\tpublic function serializedata($typ, $val)\n\t{\n\t\t$rs = '';\n\n\t\tswitch ($this->xmlrpcTypes[$typ])\n\t\t{\n\t\t\tcase 3:\n\t\t\t\t// struct\n\t\t\t\t$rs .= \"<struct>\\n\";\n\t\t\t\treset($val);\n\t\t\t\tforeach ($val as $key2 => &$val2)\n\t\t\t\t{\n\t\t\t\t\t$rs .= \"<member>\\n<name>{$key2}</name>\\n\".$this->serializeval($val2).\"</member>\\n\";\n\t\t\t\t}\n\t\t\t\t$rs .= '</struct>';\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\t// array\n\t\t\t\t$rs .= \"<array>\\n<data>\\n\";\n\t\t\t\tfor ($i = 0, $c = count($val); $i < $c; $i++)\n\t\t\t\t{\n\t\t\t\t\t$rs .= $this->serializeval($val[$i]);\n\t\t\t\t}\n\t\t\t\t$rs .= \"</data>\\n</array>\\n\";\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\t// others\n\t\t\t\tswitch ($typ)\n\t\t\t\t{\n\t\t\t\t\tcase $this->xmlrpcBase64:\n\t\t\t\t\t\t$rs .= '<'.$typ.'>'.base64_encode( (string) $val).'</'.$typ.\">\\n\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase $this->xmlrpcBoolean:\n\t\t\t\t\t\t$rs .= '<'.$typ.'>'.( (bool) $val ? '1' : '0').'</'.$typ.\">\\n\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase $this->xmlrpcString:\n\t\t\t\t\t\t$rs .= '<'.$typ.'>'.htmlspecialchars( (string) $val).'</'.$typ.\">\\n\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\t$rs .= '<'.$typ.'>'.$val.'</'.$typ.\">\\n\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\n\t\treturn $rs;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Serialize class\n\t *\n\t * @return\tstring\n\t */\n\tpublic function serialize_class()\n\t{\n\t\treturn $this->serializeval($this);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Serialize value\n\t *\n\t * @param\tobject\n\t * @return\tstring\n\t */\n\tpublic function serializeval($o)\n\t{\n\t\t$array = $o->me;\n\t\tlist($value, $type) = array(reset($array), key($array));\n\t\treturn \"<value>\\n\".$this->serializedata($type, $value).\"</value>\\n\";\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Scalar value\n\t *\n\t * @return\tmixed\n\t */\n\tpublic function scalarval()\n\t{\n\t\treturn reset($this->me);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Encode time in ISO-8601 form.\n\t * Useful for sending time in XML-RPC\n\t *\n\t * @param\tint\tunix timestamp\n\t * @param\tbool\n\t * @return\tstring\n\t */\n\tpublic function iso8601_encode($time, $utc = FALSE)\n\t{\n\t\treturn ($utc) ? date('Ymd\\TH:i:s', $time) : gmdate('Ymd\\TH:i:s', $time);\n\t}\n\n} // END XML_RPC_Values Class\n"
  },
  {
    "path": "system/libraries/Xmlrpcs.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\nif ( ! function_exists('xml_parser_create'))\n{\n\tshow_error('Your PHP installation does not support XML');\n}\n\nif ( ! class_exists('CI_Xmlrpc', FALSE))\n{\n\tshow_error('You must load the Xmlrpc class before loading the Xmlrpcs class in order to create a server.');\n}\n\n// ------------------------------------------------------------------------\n\n/**\n * XML-RPC server class\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tXML-RPC\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/xmlrpc.html\n */\nclass CI_Xmlrpcs extends CI_Xmlrpc {\n\n\t/**\n\t * Array of methods mapped to function names and signatures\n\t *\n\t * @var array\n\t */\n\tpublic $methods = array();\n\n\t/**\n\t * Debug Message\n\t *\n\t * @var string\n\t */\n\tpublic $debug_msg = '';\n\n\t/**\n\t * XML RPC Server methods\n\t *\n\t * @var array\n\t */\n\tpublic $system_methods\t= array();\n\n\t/**\n\t * Configuration object\n\t *\n\t * @var object\n\t */\n\tpublic $object = FALSE;\n\n\t/**\n\t * Initialize XMLRPC class\n\t *\n\t * @param\tarray\t$config\n\t * @return\tvoid\n\t */\n\tpublic function __construct($config = array())\n\t{\n\t\tparent::__construct();\n\t\t$this->set_system_methods();\n\n\t\tif (isset($config['functions']) && is_array($config['functions']))\n\t\t{\n\t\t\t$this->methods = array_merge($this->methods, $config['functions']);\n\t\t}\n\n\t\tlog_message('info', 'XML-RPC Server Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Initialize Prefs and Serve\n\t *\n\t * @param\tmixed\n\t * @return\tvoid\n\t */\n\tpublic function initialize($config = array())\n\t{\n\t\tif (isset($config['functions']) && is_array($config['functions']))\n\t\t{\n\t\t\t$this->methods = array_merge($this->methods, $config['functions']);\n\t\t}\n\n\t\tif (isset($config['debug']))\n\t\t{\n\t\t\t$this->debug = $config['debug'];\n\t\t}\n\n\t\tif (isset($config['object']) && is_object($config['object']))\n\t\t{\n\t\t\t$this->object = $config['object'];\n\t\t}\n\n\t\tif (isset($config['xss_clean']))\n\t\t{\n\t\t\t$this->xss_clean = $config['xss_clean'];\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Setting of System Methods\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function set_system_methods()\n\t{\n\t\t$this->methods = array(\n\t\t\t\t\t'system.listMethods'\t => array(\n\t\t\t\t\t\t\t\t\t\t'function' => 'this.listMethods',\n\t\t\t\t\t\t\t\t\t\t'signature' => array(array($this->xmlrpcArray, $this->xmlrpcString), array($this->xmlrpcArray)),\n\t\t\t\t\t\t\t\t\t\t'docstring' => 'Returns an array of available methods on this server'),\n\t\t\t\t\t'system.methodHelp'\t => array(\n\t\t\t\t\t\t\t\t\t\t'function' => 'this.methodHelp',\n\t\t\t\t\t\t\t\t\t\t'signature' => array(array($this->xmlrpcString, $this->xmlrpcString)),\n\t\t\t\t\t\t\t\t\t\t'docstring' => 'Returns a documentation string for the specified method'),\n\t\t\t\t\t'system.methodSignature' => array(\n\t\t\t\t\t\t\t\t\t\t'function' => 'this.methodSignature',\n\t\t\t\t\t\t\t\t\t\t'signature' => array(array($this->xmlrpcArray, $this->xmlrpcString)),\n\t\t\t\t\t\t\t\t\t\t'docstring' => 'Returns an array describing the return type and required parameters of a method'),\n\t\t\t\t\t'system.multicall'\t => array(\n\t\t\t\t\t\t\t\t\t\t'function' => 'this.multicall',\n\t\t\t\t\t\t\t\t\t\t'signature' => array(array($this->xmlrpcArray, $this->xmlrpcArray)),\n\t\t\t\t\t\t\t\t\t\t'docstring' => 'Combine multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details')\n\t\t\t\t);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Main Server Function\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function serve()\n\t{\n\t\t$r = $this->parseRequest();\n\t\t$payload = '<?xml version=\"1.0\" encoding=\"'.$this->xmlrpc_defencoding.'\"?'.'>'.\"\\n\".$this->debug_msg.$r->prepare_response();\n\n\t\theader('Content-Type: text/xml');\n\t\theader('Content-Length: '.strlen($payload));\n\t\texit($payload);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Add Method to Class\n\t *\n\t * @param\tstring\tmethod name\n\t * @param\tstring\tfunction\n\t * @param\tstring\tsignature\n\t * @param\tstring\tdocstring\n\t * @return\tvoid\n\t */\n\tpublic function add_to_map($methodname, $function, $sig, $doc)\n\t{\n\t\t$this->methods[$methodname] = array(\n\t\t\t'function'\t=> $function,\n\t\t\t'signature'\t=> $sig,\n\t\t\t'docstring'\t=> $doc\n\t\t);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Parse Server Request\n\t *\n\t * @param\tstring\tdata\n\t * @return\tobject\txmlrpc response\n\t */\n\tpublic function parseRequest($data = '')\n\t{\n\t\t//-------------------------------------\n\t\t//  Get Data\n\t\t//-------------------------------------\n\n\t\tif ($data === '')\n\t\t{\n\t\t\t$CI =& get_instance();\n\t\t\tif ($CI->input->method() === 'post')\n\t\t\t{\n\t\t\t\t$data = $CI->input->raw_input_stream;\n\t\t\t}\n\t\t}\n\n\t\t//-------------------------------------\n\t\t//  Set up XML Parser\n\t\t//-------------------------------------\n\n\t\t$parser = xml_parser_create($this->xmlrpc_defencoding);\n\t\t$parser_object = new XML_RPC_Message('filler');\n\t\t$pname = (string) $parser;\n\n\t\t$parser_object->xh[$pname] = array(\n\t\t\t'isf' => 0,\n\t\t\t'isf_reason' => '',\n\t\t\t'params' => array(),\n\t\t\t'stack' => array(),\n\t\t\t'valuestack' => array(),\n\t\t\t'method' => ''\n\t\t);\n\n\t\txml_set_object($parser, $parser_object);\n\t\txml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, TRUE);\n\t\txml_set_element_handler($parser, 'open_tag', 'closing_tag');\n\t\txml_set_character_data_handler($parser, 'character_data');\n\t\t//xml_set_default_handler($parser, 'default_handler');\n\n\t\t//-------------------------------------\n\t\t// PARSE + PROCESS XML DATA\n\t\t//-------------------------------------\n\n\t\tif ( ! xml_parse($parser, $data, 1))\n\t\t{\n\t\t\t// Return XML error as a faultCode\n\t\t\t$r = new XML_RPC_Response(0,\n\t\t\t\t$this->xmlrpcerrxml + xml_get_error_code($parser),\n\t\t\t\tsprintf('XML error: %s at line %d',\n\t\t\t\txml_error_string(xml_get_error_code($parser)),\n\t\t\t\txml_get_current_line_number($parser)));\n\t\t\txml_parser_free($parser);\n\t\t}\n\t\telseif ($parser_object->xh[$pname]['isf'])\n\t\t{\n\t\t\treturn new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']);\n\t\t}\n\t\telse\n\t\t{\n\t\t\txml_parser_free($parser);\n\n\t\t\t$m = new XML_RPC_Message($parser_object->xh[$pname]['method']);\n\t\t\t$plist = '';\n\n\t\t\tfor ($i = 0, $c = count($parser_object->xh[$pname]['params']); $i < $c; $i++)\n\t\t\t{\n\t\t\t\tif ($this->debug === TRUE)\n\t\t\t\t{\n\t\t\t\t\t$plist .= $i.' - '.print_r(get_object_vars($parser_object->xh[$pname]['params'][$i]), TRUE).\";\\n\";\n\t\t\t\t}\n\n\t\t\t\t$m->addParam($parser_object->xh[$pname]['params'][$i]);\n\t\t\t}\n\n\t\t\tif ($this->debug === TRUE)\n\t\t\t{\n\t\t\t\techo \"<pre>---PLIST---\\n\".$plist.\"\\n---PLIST END---\\n\\n</pre>\";\n\t\t\t}\n\n\t\t\t$r = $this->_execute($m);\n\t\t}\n\n\t\t//-------------------------------------\n\t\t// SET DEBUGGING MESSAGE\n\t\t//-------------------------------------\n\n\t\tif ($this->debug === TRUE)\n\t\t{\n\t\t\t$this->debug_msg = \"<!-- DEBUG INFO:\\n\\n\".$plist.\"\\n END DEBUG-->\\n\";\n\t\t}\n\n\t\treturn $r;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Executes the Method\n\t *\n\t * @param\tobject\n\t * @return\tmixed\n\t */\n\tprotected function _execute($m)\n\t{\n\t\t$methName = $m->method_name;\n\n\t\t// Check to see if it is a system call\n\t\t$system_call = (strpos($methName, 'system') === 0);\n\n\t\tif ($this->xss_clean === FALSE)\n\t\t{\n\t\t\t$m->xss_clean = FALSE;\n\t\t}\n\n\t\t//-------------------------------------\n\t\t// Valid Method\n\t\t//-------------------------------------\n\n\t\tif ( ! isset($this->methods[$methName]['function']))\n\t\t{\n\t\t\treturn new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);\n\t\t}\n\n\t\t//-------------------------------------\n\t\t// Check for Method (and Object)\n\t\t//-------------------------------------\n\n\t\t$method_parts = explode('.', $this->methods[$methName]['function']);\n\t\t$objectCall   = ! empty($method_parts[1]);\n\n\t\tif ($system_call === TRUE)\n\t\t{\n\t\t\tif ( ! is_callable(array($this, $method_parts[1])))\n\t\t\t{\n\t\t\t\treturn new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);\n\t\t\t}\n\t\t}\n\t\telseif (($objectCall && ( ! method_exists($method_parts[0], $method_parts[1]) OR ! (new ReflectionMethod($method_parts[0], $method_parts[1]))->isPublic()))\n\t\t\tOR ( ! $objectCall && ! is_callable($this->methods[$methName]['function']))\n\t\t)\n\t\t{\n\t\t\treturn new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);\n\t\t}\n\n\t\t//-------------------------------------\n\t\t// Checking Methods Signature\n\t\t//-------------------------------------\n\n\t\tif (isset($this->methods[$methName]['signature']))\n\t\t{\n\t\t\t$sig = $this->methods[$methName]['signature'];\n\t\t\tfor ($i = 0, $c = count($sig); $i < $c; $i++)\n\t\t\t{\n\t\t\t\t$current_sig = $sig[$i];\n\n\t\t\t\tif (count($current_sig) === count($m->params)+1)\n\t\t\t\t{\n\t\t\t\t\tfor ($n = 0, $mc = count($m->params); $n < $mc; $n++)\n\t\t\t\t\t{\n\t\t\t\t\t\t$p = $m->params[$n];\n\t\t\t\t\t\t$pt = ($p->kindOf() === 'scalar') ? $p->scalarval() : $p->kindOf();\n\n\t\t\t\t\t\tif ($pt !== $current_sig[$n+1])\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t$pno = $n+1;\n\t\t\t\t\t\t\t$wanted = $current_sig[$n+1];\n\n\t\t\t\t\t\t\treturn new XML_RPC_Response(0,\n\t\t\t\t\t\t\t\t$this->xmlrpcerr['incorrect_params'],\n\t\t\t\t\t\t\t\t$this->xmlrpcstr['incorrect_params'] .\n\t\t\t\t\t\t\t\t': Wanted '.$wanted.', got '.$pt.' at param '.$pno.')');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t//-------------------------------------\n\t\t// Calls the Function\n\t\t//-------------------------------------\n\n\t\tif ($objectCall === TRUE)\n\t\t{\n\t\t\tif ($method_parts[0] === 'this' && $system_call === TRUE)\n\t\t\t{\n\t\t\t\treturn call_user_func(array($this, $method_parts[1]), $m);\n\t\t\t}\n\t\t\telseif ($this->object === FALSE)\n\t\t\t{\n\t\t\t\treturn get_instance()->{$method_parts[1]}($m);\n\t\t\t}\n\n\t\t\treturn $this->object->{$method_parts[1]}($m);\n\t\t}\n\n\t\treturn call_user_func($this->methods[$methName]['function'], $m);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Server Function: List Methods\n\t *\n\t * @param\tmixed\n\t * @return\tobject\n\t */\n\tpublic function listMethods($m)\n\t{\n\t\t$v = new XML_RPC_Values();\n\t\t$output = array();\n\n\t\tforeach ($this->methods as $key => $value)\n\t\t{\n\t\t\t$output[] = new XML_RPC_Values($key, 'string');\n\t\t}\n\n\t\tforeach ($this->system_methods as $key => $value)\n\t\t{\n\t\t\t$output[] = new XML_RPC_Values($key, 'string');\n\t\t}\n\n\t\t$v->addArray($output);\n\t\treturn new XML_RPC_Response($v);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Server Function: Return Signature for Method\n\t *\n\t * @param\tmixed\n\t * @return\tobject\n\t */\n\tpublic function methodSignature($m)\n\t{\n\t\t$parameters = $m->output_parameters();\n\t\t$method_name = $parameters[0];\n\n\t\tif (isset($this->methods[$method_name]))\n\t\t{\n\t\t\tif ($this->methods[$method_name]['signature'])\n\t\t\t{\n\t\t\t\t$sigs = array();\n\t\t\t\t$signature = $this->methods[$method_name]['signature'];\n\n\t\t\t\tfor ($i = 0, $c = count($signature); $i < $c; $i++)\n\t\t\t\t{\n\t\t\t\t\t$cursig = array();\n\t\t\t\t\t$inSig = $signature[$i];\n\t\t\t\t\tfor ($j = 0, $jc = count($inSig); $j < $jc; $j++)\n\t\t\t\t\t{\n\t\t\t\t\t\t$cursig[]= new XML_RPC_Values($inSig[$j], 'string');\n\t\t\t\t\t}\n\t\t\t\t\t$sigs[] = new XML_RPC_Values($cursig, 'array');\n\t\t\t\t}\n\n\t\t\t\treturn new XML_RPC_Response(new XML_RPC_Values($sigs, 'array'));\n\t\t\t}\n\n\t\t\treturn new XML_RPC_Response(new XML_RPC_Values('undef', 'string'));\n\t\t}\n\n\t\treturn new XML_RPC_Response(0, $this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Server Function: Doc String for Method\n\t *\n\t * @param\tmixed\n\t * @return\tobject\n\t */\n\tpublic function methodHelp($m)\n\t{\n\t\t$parameters = $m->output_parameters();\n\t\t$method_name = $parameters[0];\n\n\t\tif (isset($this->methods[$method_name]))\n\t\t{\n\t\t\t$docstring = isset($this->methods[$method_name]['docstring']) ? $this->methods[$method_name]['docstring'] : '';\n\n\t\t\treturn new XML_RPC_Response(new XML_RPC_Values($docstring, 'string'));\n\t\t}\n\n\t\treturn new XML_RPC_Response(0, $this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Server Function: Multi-call\n\t *\n\t * @param\tmixed\n\t * @return\tobject\n\t */\n\tpublic function multicall($m)\n\t{\n\t\t// Disabled\n\t\treturn new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);\n\n\t\t$parameters = $m->output_parameters();\n\t\t$calls = $parameters[0];\n\n\t\t$result = array();\n\n\t\tforeach ($calls as $value)\n\t\t{\n\t\t\t$m = new XML_RPC_Message($value[0]);\n\t\t\t$plist = '';\n\n\t\t\tfor ($i = 0, $c = count($value[1]); $i < $c; $i++)\n\t\t\t{\n\t\t\t\t$m->addParam(new XML_RPC_Values($value[1][$i], 'string'));\n\t\t\t}\n\n\t\t\t$attempt = $this->_execute($m);\n\n\t\t\tif ($attempt->faultCode() !== 0)\n\t\t\t{\n\t\t\t\treturn $attempt;\n\t\t\t}\n\n\t\t\t$result[] = new XML_RPC_Values(array($attempt->value()), 'array');\n\t\t}\n\n\t\treturn new XML_RPC_Response(new XML_RPC_Values($result, 'array'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Multi-call Function: Error Handling\n\t *\n\t * @param\tmixed\n\t * @return\tobject\n\t */\n\tpublic function multicall_error($err)\n\t{\n\t\t$str = is_string($err) ? $this->xmlrpcstr[\"multicall_$err\"] : $err->faultString();\n\t\t$code = is_string($err) ? $this->xmlrpcerr[\"multicall_$err\"] : $err->faultCode();\n\n\t\t$struct['faultCode'] = new XML_RPC_Values($code, 'int');\n\t\t$struct['faultString'] = new XML_RPC_Values($str, 'string');\n\n\t\treturn new XML_RPC_Values($struct, 'struct');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Multi-call Function: Processes method\n\t *\n\t * @param\tmixed\n\t * @return\tobject\n\t */\n\tpublic function do_multicall($call)\n\t{\n\t\tif ($call->kindOf() !== 'struct')\n\t\t{\n\t\t\treturn $this->multicall_error('notstruct');\n\t\t}\n\t\telseif ( ! $methName = $call->me['struct']['methodName'])\n\t\t{\n\t\t\treturn $this->multicall_error('nomethod');\n\t\t}\n\n\t\tlist($scalar_value, $scalar_type) = array(reset($methName->me), key($methName->me));\n\t\t$scalar_type = $scalar_type === $this->xmlrpcI4 ? $this->xmlrpcInt : $scalar_type;\n\n\t\tif ($methName->kindOf() !== 'scalar' OR $scalar_type !== 'string')\n\t\t{\n\t\t\treturn $this->multicall_error('notstring');\n\t\t}\n\t\telseif ($scalar_value === 'system.multicall')\n\t\t{\n\t\t\treturn $this->multicall_error('recursion');\n\t\t}\n\t\telseif ( ! $params = $call->me['struct']['params'])\n\t\t{\n\t\t\treturn $this->multicall_error('noparams');\n\t\t}\n\t\telseif ($params->kindOf() !== 'array')\n\t\t{\n\t\t\treturn $this->multicall_error('notarray');\n\t\t}\n\n\t\tlist($b, $a) = array(reset($params->me), key($params->me));\n\n\t\t$msg = new XML_RPC_Message($scalar_value);\n\t\tfor ($i = 0, $numParams = count($b); $i < $numParams; $i++)\n\t\t{\n\t\t\t$msg->params[] = $params->me['array'][$i];\n\t\t}\n\n\t\t$result = $this->_execute($msg);\n\n\t\tif ($result->faultCode() !== 0)\n\t\t{\n\t\t\treturn $this->multicall_error($result);\n\t\t}\n\n\t\treturn new XML_RPC_Values(array($result->value()), 'array');\n\t}\n\n}\n"
  },
  {
    "path": "system/libraries/Zip.php",
    "content": "<?php\n/**\n * CodeIgniter\n *\n * An open source application development framework for PHP\n *\n * This content is released under the MIT License (MIT)\n *\n * Copyright (c) 2019 - 2022, CodeIgniter Foundation\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * @package\tCodeIgniter\n * @author\tEllisLab Dev Team\n * @copyright\tCopyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n * @copyright\tCopyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)\n * @copyright\tCopyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)\n * @license\thttps://opensource.org/licenses/MIT\tMIT License\n * @link\thttps://codeigniter.com\n * @since\tVersion 1.0.0\n * @filesource\n */\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n * Zip Compression Class\n *\n * This class is based on a library I found at Zend:\n * https://www.zend.com/codex.php?id=696&single=1\n *\n * The original library is a little rough around the edges so I\n * refactored it and added several additional methods -- Rick Ellis\n *\n * @package\t\tCodeIgniter\n * @subpackage\tLibraries\n * @category\tEncryption\n * @author\t\tEllisLab Dev Team\n * @link\t\thttps://codeigniter.com/userguide3/libraries/zip.html\n */\nclass CI_Zip {\n\n\t/**\n\t * Zip data in string form\n\t *\n\t * @var string\n\t */\n\tpublic $zipdata = '';\n\n\t/**\n\t * Zip data for a directory in string form\n\t *\n\t * @var string\n\t */\n\tpublic $directory = '';\n\n\t/**\n\t * Number of files/folder in zip file\n\t *\n\t * @var int\n\t */\n\tpublic $entries = 0;\n\n\t/**\n\t * Number of files in zip\n\t *\n\t * @var int\n\t */\n\tpublic $file_num = 0;\n\n\t/**\n\t * relative offset of local header\n\t *\n\t * @var int\n\t */\n\tpublic $offset = 0;\n\n\t/**\n\t * Reference to time at init\n\t *\n\t * @var int\n\t */\n\tpublic $now;\n\n\t/**\n\t * The level of compression\n\t *\n\t * Ranges from 0 to 9, with 9 being the highest level.\n\t *\n\t * @var\tint\n\t */\n\tpublic $compression_level = 2;\n\n\t/**\n\t * mbstring.func_overload flag\n\t *\n\t * @var\tbool\n\t */\n\tprotected static $func_overload;\n\n\t/**\n\t * Initialize zip compression class\n\t *\n\t * @return\tvoid\n\t */\n\tpublic function __construct()\n\t{\n\t\tisset(self::$func_overload) OR self::$func_overload = ( ! is_php('8.0') && extension_loaded('mbstring') && @ini_get('mbstring.func_overload'));\n\n\t\t$this->now = time();\n\t\tlog_message('info', 'Zip Compression Class Initialized');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Add Directory\n\t *\n\t * Lets you add a virtual directory into which you can place files.\n\t *\n\t * @param\tmixed\t$directory\tthe directory name. Can be string or array\n\t * @return\tvoid\n\t */\n\tpublic function add_dir($directory)\n\t{\n\t\tforeach ((array) $directory as $dir)\n\t\t{\n\t\t\tif ( ! preg_match('|.+/$|', $dir))\n\t\t\t{\n\t\t\t\t$dir .= '/';\n\t\t\t}\n\n\t\t\t$dir_time = $this->_get_mod_time($dir);\n\t\t\t$this->_add_dir($dir, $dir_time['file_mtime'], $dir_time['file_mdate']);\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get file/directory modification time\n\t *\n\t * If this is a newly created file/dir, we will set the time to 'now'\n\t *\n\t * @param\tstring\t$dir\tpath to file\n\t * @return\tarray\tfilemtime/filemdate\n\t */\n\tprotected function _get_mod_time($dir)\n\t{\n\t\t// filemtime() may return false, but raises an error for non-existing files\n\t\t$date = file_exists($dir) ? getdate(filemtime($dir)) : getdate($this->now);\n\n\t\treturn array(\n\t\t\t'file_mtime' => ($date['hours'] << 11) + ($date['minutes'] << 5) + $date['seconds'] / 2,\n\t\t\t'file_mdate' => (($date['year'] - 1980) << 9) + ($date['mon'] << 5) + $date['mday']\n\t\t);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Add Directory\n\t *\n\t * @param\tstring\t$dir\tthe directory name\n\t * @param\tint\t$file_mtime\n\t * @param\tint\t$file_mdate\n\t * @return\tvoid\n\t */\n\tprotected function _add_dir($dir, $file_mtime, $file_mdate)\n\t{\n\t\t$dir = str_replace('\\\\', '/', $dir);\n\n\t\t$this->zipdata .=\n\t\t\t\"\\x50\\x4b\\x03\\x04\\x0a\\x00\\x00\\x00\\x00\\x00\"\n\t\t\t.pack('v', $file_mtime)\n\t\t\t.pack('v', $file_mdate)\n\t\t\t.pack('V', 0) // crc32\n\t\t\t.pack('V', 0) // compressed filesize\n\t\t\t.pack('V', 0) // uncompressed filesize\n\t\t\t.pack('v', self::strlen($dir)) // length of pathname\n\t\t\t.pack('v', 0) // extra field length\n\t\t\t.$dir\n\t\t\t// below is \"data descriptor\" segment\n\t\t\t.pack('V', 0) // crc32\n\t\t\t.pack('V', 0) // compressed filesize\n\t\t\t.pack('V', 0); // uncompressed filesize\n\n\t\t$this->directory .=\n\t\t\t\"\\x50\\x4b\\x01\\x02\\x00\\x00\\x0a\\x00\\x00\\x00\\x00\\x00\"\n\t\t\t.pack('v', $file_mtime)\n\t\t\t.pack('v', $file_mdate)\n\t\t\t.pack('V',0) // crc32\n\t\t\t.pack('V',0) // compressed filesize\n\t\t\t.pack('V',0) // uncompressed filesize\n\t\t\t.pack('v', self::strlen($dir)) // length of pathname\n\t\t\t.pack('v', 0) // extra field length\n\t\t\t.pack('v', 0) // file comment length\n\t\t\t.pack('v', 0) // disk number start\n\t\t\t.pack('v', 0) // internal file attributes\n\t\t\t.pack('V', 16) // external file attributes - 'directory' bit set\n\t\t\t.pack('V', $this->offset) // relative offset of local header\n\t\t\t.$dir;\n\n\t\t$this->offset = self::strlen($this->zipdata);\n\t\t$this->entries++;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Add Data to Zip\n\t *\n\t * Lets you add files to the archive. If the path is included\n\t * in the filename it will be placed within a directory. Make\n\t * sure you use add_dir() first to create the folder.\n\t *\n\t * @param\tmixed\t$filepath\tA single filepath or an array of file => data pairs\n\t * @param\tstring\t$data\t\tSingle file contents\n\t * @return\tvoid\n\t */\n\tpublic function add_data($filepath, $data = NULL)\n\t{\n\t\tif (is_array($filepath))\n\t\t{\n\t\t\tforeach ($filepath as $path => $data)\n\t\t\t{\n\t\t\t\t$file_data = $this->_get_mod_time($path);\n\t\t\t\t$this->_add_data($path, $data, $file_data['file_mtime'], $file_data['file_mdate']);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$file_data = $this->_get_mod_time($filepath);\n\t\t\t$this->_add_data($filepath, $data, $file_data['file_mtime'], $file_data['file_mdate']);\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Add Data to Zip\n\t *\n\t * @param\tstring\t$filepath\tthe file name/path\n\t * @param\tstring\t$data\tthe data to be encoded\n\t * @param\tint\t$file_mtime\n\t * @param\tint\t$file_mdate\n\t * @return\tvoid\n\t */\n\tprotected function _add_data($filepath, $data, $file_mtime, $file_mdate)\n\t{\n\t\t$filepath = str_replace('\\\\', '/', $filepath);\n\n\t\t$uncompressed_size = self::strlen($data);\n\t\t$crc32  = crc32($data);\n\t\t$gzdata = self::substr(gzcompress($data, $this->compression_level), 2, -4);\n\t\t$compressed_size = self::strlen($gzdata);\n\n\t\t$this->zipdata .=\n\t\t\t\"\\x50\\x4b\\x03\\x04\\x14\\x00\\x00\\x00\\x08\\x00\"\n\t\t\t.pack('v', $file_mtime)\n\t\t\t.pack('v', $file_mdate)\n\t\t\t.pack('V', $crc32)\n\t\t\t.pack('V', $compressed_size)\n\t\t\t.pack('V', $uncompressed_size)\n\t\t\t.pack('v', self::strlen($filepath)) // length of filename\n\t\t\t.pack('v', 0) // extra field length\n\t\t\t.$filepath\n\t\t\t.$gzdata; // \"file data\" segment\n\n\t\t$this->directory .=\n\t\t\t\"\\x50\\x4b\\x01\\x02\\x00\\x00\\x14\\x00\\x00\\x00\\x08\\x00\"\n\t\t\t.pack('v', $file_mtime)\n\t\t\t.pack('v', $file_mdate)\n\t\t\t.pack('V', $crc32)\n\t\t\t.pack('V', $compressed_size)\n\t\t\t.pack('V', $uncompressed_size)\n\t\t\t.pack('v', self::strlen($filepath)) // length of filename\n\t\t\t.pack('v', 0) // extra field length\n\t\t\t.pack('v', 0) // file comment length\n\t\t\t.pack('v', 0) // disk number start\n\t\t\t.pack('v', 0) // internal file attributes\n\t\t\t.pack('V', 32) // external file attributes - 'archive' bit set\n\t\t\t.pack('V', $this->offset) // relative offset of local header\n\t\t\t.$filepath;\n\n\t\t$this->offset = self::strlen($this->zipdata);\n\t\t$this->entries++;\n\t\t$this->file_num++;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Read the contents of a file and add it to the zip\n\t *\n\t * @param\tstring\t$path\n\t * @param\tbool\t$archive_filepath\n\t * @return\tbool\n\t */\n\tpublic function read_file($path, $archive_filepath = FALSE)\n\t{\n\t\tif (file_exists($path) && FALSE !== ($data = file_get_contents($path)))\n\t\t{\n\t\t\tif (is_string($archive_filepath))\n\t\t\t{\n\t\t\t\t$name = str_replace('\\\\', '/', $archive_filepath);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$name = str_replace('\\\\', '/', $path);\n\n\t\t\t\tif ($archive_filepath === FALSE)\n\t\t\t\t{\n\t\t\t\t\t$name = preg_replace('|.*/(.+)|', '\\\\1', $name);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$this->add_data($name, $data);\n\t\t\treturn TRUE;\n\t\t}\n\n\t\treturn FALSE;\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Read a directory and add it to the zip.\n\t *\n\t * This function recursively reads a folder and everything it contains (including\n\t * sub-folders) and creates a zip based on it. Whatever directory structure\n\t * is in the original file path will be recreated in the zip file.\n\t *\n\t * @param\tstring\t$path\tpath to source directory\n\t * @param\tbool\t$preserve_filepath\n\t * @param\tstring\t$root_path\n\t * @return\tbool\n\t */\n\tpublic function read_dir($path, $preserve_filepath = TRUE, $root_path = NULL)\n\t{\n\t\t$path = rtrim($path, '/\\\\').DIRECTORY_SEPARATOR;\n\t\tif ( ! $fp = @opendir($path))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// Set the original directory root for child dir's to use as relative\n\t\tif ($root_path === NULL)\n\t\t{\n\t\t\t$root_path = str_replace(array('\\\\', '/'), DIRECTORY_SEPARATOR, dirname($path)).DIRECTORY_SEPARATOR;\n\t\t}\n\n\t\twhile (FALSE !== ($file = readdir($fp)))\n\t\t{\n\t\t\tif ($file === '.' OR $file === '..')\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (is_dir($path.$file))\n\t\t\t{\n\t\t\t\t$this->read_dir($path.$file.DIRECTORY_SEPARATOR, $preserve_filepath, $root_path);\n\t\t\t}\n\t\t\telseif (FALSE !== ($data = file_get_contents($path.$file)))\n\t\t\t{\n\t\t\t\t$name = str_replace(array('\\\\', '/'), DIRECTORY_SEPARATOR, $path);\n\t\t\t\tif ($preserve_filepath === FALSE)\n\t\t\t\t{\n\t\t\t\t\t$name = str_replace($root_path, '', $name);\n\t\t\t\t}\n\n\t\t\t\t$this->add_data($name.$file, $data);\n\t\t\t}\n\t\t}\n\n\t\tclosedir($fp);\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Get the Zip file\n\t *\n\t * @return\tstring\t(binary encoded)\n\t */\n\tpublic function get_zip()\n\t{\n\t\t// Is there any data to return?\n\t\tif ($this->entries === 0)\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\t// @see https://github.com/bcit-ci/CodeIgniter/issues/5864\n\t\t$footer = $this->directory.\"\\x50\\x4b\\x05\\x06\\x00\\x00\\x00\\x00\"\n\t\t\t.pack('v', $this->entries) // total # of entries \"on this disk\"\n\t\t\t.pack('v', $this->entries) // total # of entries overall\n\t\t\t.pack('V', self::strlen($this->directory)) // size of central dir\n\t\t\t.pack('V', self::strlen($this->zipdata)) // offset to start of central dir\n\t\t\t.\"\\x00\\x00\"; // .zip file comment length\n\t\treturn $this->zipdata.$footer;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Write File to the specified directory\n\t *\n\t * Lets you write a file\n\t *\n\t * @param\tstring\t$filepath\tthe file name\n\t * @return\tbool\n\t */\n\tpublic function archive($filepath)\n\t{\n\t\tif ( ! ($fp = @fopen($filepath, 'w+b')))\n\t\t{\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tflock($fp, LOCK_EX);\n\n\t\tfor ($result = $written = 0, $data = $this->get_zip(), $length = self::strlen($data); $written < $length; $written += $result)\n\t\t{\n\t\t\tif (($result = fwrite($fp, self::substr($data, $written))) === FALSE)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tflock($fp, LOCK_UN);\n\t\tfclose($fp);\n\n\t\treturn is_int($result);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Download\n\t *\n\t * @param\tstring\t$filename\tthe file name\n\t * @return\tvoid\n\t */\n\tpublic function download($filename = 'backup.zip')\n\t{\n\t\tif ( ! preg_match('|.+?\\.zip$|', $filename))\n\t\t{\n\t\t\t$filename .= '.zip';\n\t\t}\n\n\t\tget_instance()->load->helper('download');\n\t\t$get_zip = $this->get_zip();\n\t\t$zip_content =& $get_zip;\n\n\t\tforce_download($filename, $zip_content);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Initialize Data\n\t *\n\t * Lets you clear current zip data. Useful if you need to create\n\t * multiple zips with different data.\n\t *\n\t * @return\tCI_Zip\n\t */\n\tpublic function clear_data()\n\t{\n\t\t$this->zipdata = '';\n\t\t$this->directory = '';\n\t\t$this->entries = 0;\n\t\t$this->file_num = 0;\n\t\t$this->offset = 0;\n\t\treturn $this;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Byte-safe strlen()\n\t *\n\t * @param\tstring\t$str\n\t * @return\tint\n\t */\n\tprotected static function strlen($str)\n\t{\n\t\treturn (self::$func_overload)\n\t\t\t? mb_strlen($str, '8bit')\n\t\t\t: strlen($str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Byte-safe substr()\n\t *\n\t * @param\tstring\t$str\n\t * @param\tint\t$start\n\t * @param\tint\t$length\n\t * @return\tstring\n\t */\n\tprotected static function substr($str, $start, $length = NULL)\n\t{\n\t\tif (self::$func_overload)\n\t\t{\n\t\t\treturn mb_substr($str, $start, $length, '8bit');\n\t\t}\n\n\t\treturn isset($length)\n\t\t\t? substr($str, $start, $length)\n\t\t\t: substr($str, $start);\n\t}\n}\n"
  },
  {
    "path": "system/libraries/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n</head>\n<body>\n\n<p>Directory access is forbidden.</p>\n\n</body>\n</html>\n"
  },
  {
    "path": "tests/Bootstrap.php",
    "content": "<?php\n// Errors on full!\nini_set('display_errors', 1);\nerror_reporting(E_ALL | E_STRICT);\n\n$dir = realpath(dirname(__FILE__));\n\n// Path constants\ndefined('PROJECT_BASE') OR define('PROJECT_BASE', realpath($dir.'/../').'/');\ndefined('SYSTEM_PATH') OR define('SYSTEM_PATH', PROJECT_BASE.'system/');\n\n// Get vfsStream either via PEAR or composer\nforeach (explode(PATH_SEPARATOR, get_include_path()) as $path)\n{\n\tif (file_exists($path.DIRECTORY_SEPARATOR.'vfsStream/vfsStream.php'))\n\t{\n\t\trequire_once 'vfsStream/vfsStream.php';\n\t\tbreak;\n\t}\n}\n\nif ( ! class_exists('vfsStream') && file_exists(PROJECT_BASE.'vendor/autoload.php'))\n{\n\tinclude_once PROJECT_BASE.'vendor/autoload.php';\n\tclass_alias('org\\bovigo\\vfs\\vfsStream', 'vfsStream');\n\tclass_alias('org\\bovigo\\vfs\\vfsStreamDirectory', 'vfsStreamDirectory');\n\tclass_alias('org\\bovigo\\vfs\\vfsStreamWrapper', 'vfsStreamWrapper');\n}\n\n// Define CI path constants to VFS (filesystem setup in CI_TestCase::setUp)\ndefined('BASEPATH') OR define('BASEPATH', vfsStream::url('system/'));\ndefined('APPPATH') OR define('APPPATH', vfsStream::url('application/'));\ndefined('VIEWPATH') OR define('VIEWPATH', APPPATH.'views/');\ndefined('ENVIRONMENT') OR define('ENVIRONMENT', 'development');\n\n// Set localhost \"remote\" IP\nisset($_SERVER['REMOTE_ADDR']) OR $_SERVER['REMOTE_ADDR'] = '127.0.0.1';\n\n// Prep our test environment\ninclude_once $dir.'/mocks/core/common.php';\ninclude_once SYSTEM_PATH.'core/Common.php';\n\nini_set('default_charset', 'UTF-8');\n\nif (extension_loaded('mbstring'))\n{\n\tdefined('MB_ENABLED') OR define('MB_ENABLED', TRUE);\n\t@ini_set('mbstring.internal_encoding', 'UTF-8');\n\tmb_substitute_character('none');\n}\nelse\n{\n\tdefined('MB_ENABLED') OR define('MB_ENABLED', FALSE);\n}\n\nif (extension_loaded('iconv'))\n{\n\tdefined('ICONV_ENABLED') OR define('ICONV_ENABLED', TRUE);\n\t@ini_set('iconv.internal_encoding', 'UTF-8');\n}\nelse\n{\n\tdefined('ICONV_ENABLED') OR define('ICONV_ENABLED', FALSE);\n}\n\nis_php('5.6') && ini_set('php.internal_encoding', 'UTF-8');\n\nif (is_php('7.0'))\n{\n\t$test_case_code = file_get_contents(PROJECT_BASE.'vendor/phpunit/phpunit/src/Framework/TestCase.php');\n\t$test_case_code = preg_replace('/^\\s+((?:protected|public)(?: static)? function \\w+\\(\\)): void/m', '$1', $test_case_code);\n\tfile_put_contents(PROJECT_BASE.'vendor/phpunit/phpunit/src/Framework/TestCase.php', $test_case_code);\n}\n\ninclude_once SYSTEM_PATH.'core/compat/mbstring.php';\ninclude_once SYSTEM_PATH.'core/compat/hash.php';\ninclude_once SYSTEM_PATH.'core/compat/password.php';\ninclude_once SYSTEM_PATH.'core/compat/standard.php';\n\ninclude_once $dir.'/mocks/autoloader.php';\nspl_autoload_register('autoload');\n\nunset($dir);"
  },
  {
    "path": "tests/README.md",
    "content": "# CodeIgniter Unit Tests #\n\nStatus : [![Build Status](https://secure.travis-ci.org/bcit-ci/CodeIgniter.png?branch=develop)](https://travis-ci.org/bcit-ci/CodeIgniter)\n\n### Introduction:\n\nThis is the preliminary CodeIgniter testing documentation. It\nwill cover both internal as well as external APIs and the reasoning\nbehind their implementation, where appropriate. As with all CodeIgniter\ndocumentation, this file should maintain a mostly human readable\nformat to facilitate clean api design. [see https://arrenbrecht.ch/testing/]\n\n*First public draft: everything is subject to change*\n\n### Requirements\n\nPHP Unit >= 3.5.6\n\n\tpear channel-discover pear.phpunit.de\n\tpear channel-discover pear.symfony.com\n\tpear install phpunit/PHPUnit\n\nvfsStream\n\n\tpear channel-discover pear.bovigo.org\n\tpear install bovigo/vfsStream-beta\n\n#### Installation of PEAR and PHPUnit on Ubuntu\n\n  Installation on Ubuntu requires a few steps. Depending on your setup you may\n  need to use 'sudo' to install these. Mileage may vary but these steps are a\n  good start.\n\n\t# Install the PEAR package\n\tsudo apt-get install php-pear\n\n\t# Add a few sources to PEAR\n\tpear channel-discover pear.phpunit.de\n\tpear channel-discover pear.symfony-project.com\n\tpear channel-discover components.ez.no\n\tpear channel-discover pear.bovigo.org\n\n\t# Finally install PHPUnit and vfsStream (including dependencies)\n\tpear install --alldeps phpunit/PHPUnit\n\tpear install --alldeps bovigo/vfsStream-beta\n\n\t# Finally, run 'phpunit' from within the ./tests directory\n\t# and you should be on your way!\n\n## Test Suites:\n\nCodeIgniter bootstraps a request very directly, with very flat class\nhierarchy. As a result, there is no main CodeIgniter class until the\ncontroller is instantiated.\n\nThis has forced the core classes to be relatively decoupled, which is\na good thing. However, it makes that portion of code relatively hard\nto test.\n\nRight now that means we'll probably have two core test suites, along\nwith a base for application and package tests. That gives us:\n\n1. Bootstrap Test\t- test common.php and sanity check codeigniter.php [in planning]\n2. System Test\t\t- test core components in relative isolation [in development]\n3. Application Test\t- bootstrapping for application/tests [not started]\n4. Package Test\t\t- bootstrapping for <package>/tests [not started]\n\n### Test Environment:\n\nThe test/Bootstrap.php file establishes global constants such as BASEPATH,\nAPPPATH, and VIEWPATH, initializing them to point to VFS locations. The\ntest case class employs vfsStream to make a clean virtual filesystem with\nthe necessary paths for every individual test.\n\nWithin each test case, VFS directory objects are available to use as arguments\nto the VFS convenience functions (see below):\n\n- ci_vfs_root: VFS filesystem root\n- ci_app_root: Application directory\n- ci_base_root: System directory\n- ci_view_root: Views directory\n\nClasses being instantiated for testing are read from the actual filesystem\nby the unit test autoloader, as are mockups created in tests/mocks. If you\nneed access to the real system directory, the SYSTEM_PATH constant always\npoints to it.\n\nAny other resources which need to be read from the path constants must be\ncreated or cloned within your test. Functions for doing so are outlined\nbelow.\n\n### CI_TestCase Documentation\n\nTest cases should extend CI_TestCase. This internally extends\nPHPUnit\\_Framework\\_TestCase, so you have access to all of your\nusual PHPUnit methods.\n\nWe need to provide a simple way to modify the globals and the\ncommon function output. We also need to be able to mock up\nthe super object as we please.\n\nCurrent API is *not stable*. Names and implementations will change.\n\n    $this->ci_set_config($key, $val)\n\nSet the global config variables in a mock Config object. If key is an array,\nit will replace the entire config array. They are _not_ merged. If called\nwithout any parameters, it will create the mock object but not set any values.\nThe mock Config object also provides rudimentary item() and load() stubs for\ndelivering configured values to classes being tested and handling config load\ncalls, respectively. The load() stub does _not_ actually load any files, it\nonly records the filename provided. Check the config->loaded array to verify\ncalls made.\n\n    $this->ci_instance($obj)\n\nSet the object to use as the \"super object\", in a lot\nof cases this will be a simple stdClass with the attributes\nyou need it to have. If no parameter, will return the instance.\n\n\t$this->ci_instance_var($name, $val)\n\nAdd an attribute to the super object. This is useful if you\nset up a simple instance in setUp and then need to add different\nclass mockups to your super object.\n\n\t$this->ci_core_class($name)\n\nGet the _class name_ of a core class, so that you can instantiate\nit. The variable is returned by reference and is tied to the correct\n$GLOBALS key. For example:\n    \n\t$cfg =& $this->ci_core_class('cfg'); // returns 'CI_Config'\n    $cfg = new $cfg; // instantiates config and overwrites the CFG global\n\n\t$this->ci_set_core_class($name, $obj)\n\nAn alternative way to set one of the core globals.\n\n\t$this->ci_vfs_mkdir($name, $root)\n\nCreates a new directory in the test VFS. Pass a directory object to be the\nparent directory or none to create a root-level directory. Returns the new\ndirectory object.\n\n\t$this->ci_vfs_create($file, $content, $root, $path)\n\nCreates a new VFS file. '.php' is automatically appended to the filename if\nit has no extension. Pass a directory object as the root, and an optional path\nto recurse and/or create for containing the file. Path may be a string (such\nas 'models/subdir') or an array (e.g. - array('models', 'subdir') ). Existing\ndirectories in the VFS root will be recursed until a new directory is\nidentified - all others in the path will be created, so you can mix-and-match\nold and new directories. If $file is an array (key = name, value = content),\nmultiple files will be created in the same path.\n\n\t$this->ci_vfs_clone($path)\n\nClones an existing file from the real filesystem to exist in the same path of\nthe VFS. Path must be relative to the project root (i.e. - starting with\n'system' or 'application').\n\n\t$this->ci_vfs_path($path, $base)\n\nCreates a VFS file path string suitable for use with PHP file operations. Path\nmay be absolute from the VFS root, or relative to a base path. It is often\nuseful to use APPPATH or BASEPATH as the base.\n\n\t$this->helper($name)\n\nLoads a helper from the real filesystem.\n\n\t$this->lang($name)\n\nLoads a language file from the real filesystem and returns the $lang array.\n\n\t$this->ci_get_config()  __internal__\n\nReturns the global config array. Internal as you shouldn't need to\ncall this (you're setting it, after all). Used internally to make\nCI's get_config() work.\n\n\tCI_TestCase::instance()  __internal__\n\nReturns an instance of the current test case. We force phpunit to\nrun with backup-globals enabled, so this will always be the instance\nof the currently running test class.\n\n### Going forward\n\n#### 1. Bootstrap Test\n\nTesting common.php should be pretty simple. Include the file, and test the\nfunctions. May require some tweaking so that we can grab the statics from all\nmethods (see is_loaded()). Testing the actual CodeIgniter.php file will most\nlikely be an output test for the default view, with some object checking after\nthe file runs. Needs consideration.\n\n#### 2. System Test\n\nTesting the core system relies on being able to isolate the core components\nas much as possible. A few of them access other core classes as globals. These\nshould be mocked up and easy to manipulate.\n\nAll functions in common.php should be a minimal implementation, or and mapped\nto a method in the test's parent class to gives us full control of their output.\n\n#### 3. Application Test:\n\nNot sure yet, needs to handle:\n\n- Libraries\n- Helpers\n- Models\n- MY_* files\n- Controllers (uh...?)\n- Views? (watir, selenium, cucumber?)\n- Database Testing\n\n#### 4. Package Test:\n\nI don't have a clue how this will work.\n\nNeeds to be able to handle packages\nthat are used multiple times within the application (i.e. EE/Pyro modules)\nas well as packages that are used by multiple applications (library distributions)"
  },
  {
    "path": "tests/codeigniter/Setup_test.php",
    "content": "<?php\n\nclass Setup_test extends \\PHPUnit\\Framework\\TestCase {\n\n\tpublic function test_bootstrap_constants()\n\t{\n\t\t$this->assertTrue(defined('PROJECT_BASE'));\n\t\t$this->assertTrue(defined('BASEPATH'));\n\t\t$this->assertTrue(defined('APPPATH'));\n\t\t$this->assertTrue(defined('VIEWPATH'));\n\t}\n\n}"
  },
  {
    "path": "tests/codeigniter/core/Benchmark_test.php",
    "content": "<?php\n\nclass Benchmark_test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t$this->benchmark = new CI_Benchmark();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_mark()\n\t{\n\t\t$this->assertEmpty($this->benchmark->marker);\n\n\t\t$this->benchmark->mark('code_start');\n\n\t\t$this->assertCount(1, $this->benchmark->marker);\n\t\t$this->assertArrayHasKey('code_start', $this->benchmark->marker);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_elapsed_time()\n\t{\n\t\t$this->assertEquals('{elapsed_time}', $this->benchmark->elapsed_time());\n\t\t$this->assertEmpty($this->benchmark->elapsed_time('undefined_point'));\n\n\t\t$this->benchmark->mark('code_start');\n\t\t$this->benchmark->mark('code_end');\n\n\t\t// Override values, because time isn't testable, but make sure the markers were set\n\t\tif (isset($this->benchmark->marker['code_start']) && is_float($this->benchmark->marker['code_start']))\n\t\t{\n\t\t\t$this->benchmark->marker['code_start'] = 1389956144.1944;\n\t\t}\n\n\t\tif (isset($this->benchmark->marker['code_end']) && is_float($this->benchmark->marker['code_end']))\n\t\t{\n\t\t\t$this->benchmark->marker['code_end'] = 1389956145.1946;\n\t\t}\n\n\t\t$this->assertEquals('1', $this->benchmark->elapsed_time('code_start', 'code_end', 0));\n\t\t$this->assertEquals('1.0', $this->benchmark->elapsed_time('code_start', 'code_end', 1));\n\t\t$this->assertEquals('1.00', $this->benchmark->elapsed_time('code_start', 'code_end', 2));\n\t\t$this->assertEquals('1.000', $this->benchmark->elapsed_time('code_start', 'code_end', 3));\n\t\t$this->assertEquals('1.0002', $this->benchmark->elapsed_time('code_start', 'code_end', 4));\n\t\t$this->assertEquals('1.0002', $this->benchmark->elapsed_time('code_start', 'code_end'));\n\n\t\t// Test with non-existing 2nd marker, but again - we need to override the value\n\t\t$this->benchmark->elapsed_time('code_start', 'code_end2');\n\t\tif (isset($this->benchmark->marker['code_end2']) && is_float($this->benchmark->marker['code_end2']))\n\t\t{\n\t\t\t$this->benchmark->marker['code_end2'] = 1389956146.2046;\n\t\t}\n\n\t\t$this->assertEquals('2.0102', $this->benchmark->elapsed_time('code_start', 'code_end2'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_memory_usage()\n\t{\n\t\t$this->assertEquals('{memory_usage}', $this->benchmark->memory_usage());\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/core/Common_test.php",
    "content": "<?php\n\nclass Common_test extends CI_TestCase {\n\n\tpublic function test_is_php()\n\t{\n\t\t$this->assertTrue(is_php('1.2.0'));\n\t\t$this->assertFalse(is_php('9999.9.9'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_stringify_attributes()\n\t{\n\t\t$this->assertEquals(' class=\"foo\" id=\"bar\"', _stringify_attributes(array('class' => 'foo', 'id' => 'bar')));\n\n\t\t$atts = new stdClass;\n\t\t$atts->class = 'foo';\n\t\t$atts->id = 'bar';\n\t\t$this->assertEquals(' class=\"foo\" id=\"bar\"', _stringify_attributes($atts));\n\n\t\t$atts = new stdClass;\n\t\t$this->assertEquals('', _stringify_attributes($atts));\n\n\t\t$this->assertEquals(' class=\"foo\" id=\"bar\"', _stringify_attributes('class=\"foo\" id=\"bar\"'));\n\n\t\t$this->assertEquals('', _stringify_attributes(array()));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_stringify_js_attributes()\n\t{\n\t\t$this->assertEquals('width=800,height=600', _stringify_attributes(array('width' => '800', 'height' => '600'), TRUE));\n\n\t\t$atts = new stdClass;\n\t\t$atts->width = 800;\n\t\t$atts->height = 600;\n\t\t$this->assertEquals('width=800,height=600', _stringify_attributes($atts, TRUE));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_html_escape()\n\t{\n\t\t$this->assertEquals(\n\t\t\thtml_escape('Here is a string containing \"quoted\" text.'),\n\t\t\t'Here is a string containing &quot;quoted&quot; text.'\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\thtml_escape(array('associative' => 'and', array('multi' => 'dimentional'))),\n\t\t\tarray('associative' => 'and', array('multi' => 'dimentional'))\n\t\t);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_remove_invisible_characters()\n\t{\n\t\t$raw_string = 'Here is a string containing invisible'.chr(0x08).' text %0e.';\n\t\t$removed_string = 'Here is a string containing invisible text %0e.';\n\t\t$this->assertEquals($removed_string, remove_invisible_characters($raw_string, FALSE));\n\n\t\t$raw_string = 'Here is a string %0econtaining url_encoded invisible%1F text.';\n\t\t$removed_string = 'Here is a string containing url_encoded invisible text.';\n\t\t$this->assertEquals($removed_string, remove_invisible_characters($raw_string));\n\t}\n}\n"
  },
  {
    "path": "tests/codeigniter/core/Config_test.php",
    "content": "<?php\n\nclass Config_test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t$cls =& $this->ci_core_class('cfg');\n\n\t\t// set predictable config values\n\t\t$this->cfg = array(\n\t\t\t'index_page'\t\t=> 'index.php',\n\t\t\t'base_url'\t\t=> 'http://example.com/',\n\t\t\t'subclass_prefix'\t=> 'MY_'\n\t\t);\n\t\t$this->ci_set_config($this->cfg);\n\n\t\t$this->config = new $cls;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_item()\n\t{\n\t\t$this->assertEquals($this->cfg['base_url'], $this->config->item('base_url'));\n\n\t\t// Bad Config value\n\t\t$this->assertNull($this->config->item('no_good_item'));\n\n\t\t// Index\n\t\t$this->assertNull($this->config->item('no_good_item', 'bad_index'));\n\t\t$this->assertNull($this->config->item('no_good_item', 'default'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_set_item()\n\t{\n\t\t$this->assertNull($this->config->item('not_yet_set'));\n\n\t\t$this->config->set_item('not_yet_set', 'is set');\n\t\t$this->assertEquals('is set', $this->config->item('not_yet_set'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_slash_item()\n\t{\n\t\t// Bad Config value\n\t\t$this->assertNull($this->config->slash_item('no_good_item'));\n\n\t\t$this->assertEquals($this->cfg['base_url'], $this->config->slash_item('base_url'));\n\t\t$this->assertEquals($this->cfg['subclass_prefix'].'/', $this->config->slash_item('subclass_prefix'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_base_url()\n\t{\n\t\t// Test regular base URL\n\t\t$base_url = $this->cfg['base_url'];\n\t\t$this->assertEquals($base_url, $this->config->base_url());\n\n\t\t// Test with URI\n\t\t$uri = 'test';\n\t\t$this->assertEquals($base_url.$uri, $this->config->base_url($uri));\n\n\t\t// Clear base_url\n\t\t$this->ci_set_config('base_url', '');\n\n\t\t// Rerun constructor\n\t\t$cls =& $this->ci_core_class('cfg');\n\t\t$this->config = new $cls;\n\n\t\t// Test default base\n\t\t$this->assertEquals('http://localhost/', $this->config->base_url());\n\n\t\t// Capture server vars\n\t\t$old_host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : NULL;\n\t\t$old_script_name = isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : NULL;\n\t\t$old_script_filename = $_SERVER['SCRIPT_FILENAME'];\n\t\t$old_https = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : NULL;\n\t\t$old_server_addr = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : NULL;\n\n\t\t// The 'Host' header is user input and must not be trusted\n\t\t$_SERVER['HTTP_HOST'] = 'test.com';\n\t\t$this->config = new $cls;\n\t\t$this->assertEquals('http://localhost/', $this->config->base_url());\n\n\t\t// However, we may fallback to the server's IP address\n\t\t$_SERVER['SERVER_ADDR'] = '127.0.0.1';\n\t\t$_SERVER['SCRIPT_NAME'] = '/base_test.php';\n\t\t$_SERVER['SCRIPT_FILENAME'] = '/foo/bar/base_test.php';\n\t\t$this->config = new $cls;\n\t\t$this->assertEquals('http://127.0.0.1/', $this->config->base_url());\n\n\t\t// Making sure that HTTPS and URI path are also detected\n\t\t$_SERVER['HTTPS'] = 'on';\n\t\t$_SERVER['SCRIPT_NAME'] = '/path/base_test.php';\n\t\t$_SERVER['SCRIPT_FILENAME'] = '/foo/bar/path/base_test.php';\n\t\t$this->config = new $cls;\n\t\t$this->assertEquals('https://127.0.0.1/path/', $this->config->base_url());\n\n\t\t// Restore server vars\n\t\t$_SERVER['HTTP_HOST'] = $old_host;\n\t\t$_SERVER['SCRIPT_NAME'] = $old_script_name;\n\t\t$_SERVER['SCRIPT_FILENAME'] = $old_script_filename;\n\t\t$_SERVER['HTTPS'] = $old_https;\n\t\t$_SERVER['SERVER_ADDR'] = $old_server_addr;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_site_url()\n\t{\n\t\t$base_url = $this->cfg['base_url'];\n\t\t$index_page = $this->cfg['index_page'];\n\t\t$this->assertEquals($base_url.$index_page, $this->config->site_url());\n\n\t\t$old_base = $this->config->item('base_url');\n\t\t$this->config->set_item('base_url', '');\n\n\t\t$q_string = $this->config->item('enable_query_strings');\n\t\t$this->config->set_item('enable_query_strings', FALSE);\n\n\t\t$uri = 'test';\n\t\t$uri2 = '1';\n\t\t$this->assertEquals($index_page.'/'.$uri, $this->config->site_url($uri));\n\t\t$this->assertEquals($index_page.'/'.$uri.'/'.$uri2, $this->config->site_url(array($uri, $uri2)));\n\n\t\t$this->assertEquals($index_page.'/test/', $this->config->site_url('test/'));\n\n\t\t$suffix = 'ing';\n\t\t$this->config->set_item('url_suffix', $suffix);\n\n\t\t$arg = 'pass';\n\t\t$this->assertEquals($index_page.'/'.$uri.$suffix, $this->config->site_url($uri));\n\t\t$this->assertEquals($index_page.'/'.$uri.$suffix.'?'.$arg, $this->config->site_url($uri.'?'.$arg));\n\n\t\t$this->config->set_item('url_suffix', FALSE);\n\t\t$this->config->set_item('enable_query_strings', TRUE);\n\n\t\t$this->assertEquals($index_page.'?'.$uri, $this->config->site_url($uri));\n\t\t$this->assertEquals($index_page.'?0='.$uri.'&1='.$uri2, $this->config->site_url(array($uri, $uri2)));\n\n\t\t$this->config->set_item('base_url', $old_base);\n\n\t\t$this->assertEquals($base_url.$index_page.'?'.$uri, $this->config->site_url($uri));\n\n\t\t// back to home base\n\t\t$this->config->set_item('enable_query_strings', $q_string);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_load()\n\t{\n\t\t// Test regular load\n\t\t$file = 'test.php';\n\t\t$key = 'testconfig';\n\t\t$val = 'my_value';\n\t\t$cfg = array($key => $val);\n\t\t$this->ci_vfs_create($file, '<?php $config = '.var_export($cfg, TRUE).';', $this->ci_app_root, 'config');\n\t\t$this->assertTrue($this->config->load($file));\n\t\t$this->assertEquals($val, $this->config->item($key));\n\n\t\t// Test reload - value should not change\n\t\t$val2 = 'new_value';\n\t\t$cfg = array($key => $val2);\n\t\t$this->ci_vfs_create($file, '<?php $config = '.var_export($cfg, TRUE).';', $this->ci_app_root, 'config');\n\t\t$this->assertTrue($this->config->load($file));\n\t\t$this->assertEquals($val, $this->config->item($key));\n\n\t\t// Test section load\n\t\t$file = 'secttest';\n\t\t$cfg = array(\n\t\t\t'one' => 'prime',\n\t\t\t'two' => 2,\n\t\t\t'three' => TRUE\n\t\t);\n\t\t$this->ci_vfs_create($file.'.php', '<?php $config = '.var_export($cfg, TRUE).';', $this->ci_app_root, 'config');\n\t\t$this->assertTrue($this->config->load($file, TRUE));\n\t\t$this->assertEquals($cfg, $this->config->item($file));\n\n\t\t// Test section merge\n\t\t$cfg2 = array(\n\t\t\t'three' => 'tres',\n\t\t\t'number' => 42,\n\t\t\t'letter' => 'Z'\n\t\t);\n\n\t\t$pkg_dir = 'package';\n\t\t$this->ci_vfs_create(\n\t\t\t$file.'.php',\n\t\t\t'<?php $config = '.var_export($cfg2, TRUE).';',\n\t\t\t$this->ci_app_root,\n\t\t\tarray($pkg_dir, 'config')\n\t\t);\n\t\tarray_unshift($this->config->_config_paths, $this->ci_vfs_path($pkg_dir.'/', APPPATH));\n\t\t$this->assertTrue($this->config->load($file, TRUE));\n\t\t$this->assertEquals(array_merge($cfg, $cfg2), $this->config->item($file));\n\t\tarray_shift($this->config->_config_paths);\n\n\t\t// Test graceful fail of invalid file\n\t\t$file = 'badfile';\n\t\t$this->ci_vfs_create($file, '', $this->ci_app_root, 'config');\n\t\t$this->assertFalse($this->config->load($file, FALSE, TRUE));\n\n\t\t// Test regular fail of invalid file\n\t\t$this->setExpectedException(\n\t\t\t'RuntimeException',\n\t\t\t'CI Error: Your '.$this->ci_vfs_path('config/'.$file.'.php', APPPATH).\n\t\t\t\t' file does not appear to contain a valid configuration array.'\n\t\t);\n\t\t$this->assertNull($this->config->load($file));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_load_nonexistent()\n\t{\n\t\t// Test graceful fail of nonexistent file\n\t\t$this->assertFalse($this->config->load('not_config_file', FALSE, TRUE));\n\n\t\t// Test regular fail\n\t\t$file = 'absentia';\n\t\t$this->setExpectedException(\n\t\t\t'RuntimeException',\n\t\t\t'CI Error: The configuration file '.$file.'.php does not exist.'\n\t\t);\n\t\t$this->assertNull($this->config->load($file));\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/core/Input_test.php",
    "content": "<?php\n\nclass Input_test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t// Set server variable to GET as default, since this will leave unset in STDIN env\n\t\t$_SERVER['REQUEST_METHOD'] = 'GET';\n\n\t\t// Set config for Input class\n\t\t$this->ci_set_config('allow_get_array',\tTRUE);\n\t\t$this->ci_set_config('global_xss_filtering', FALSE);\n\t\t$this->ci_set_config('csrf_protection', FALSE);\n\n\t\t$security = new Mock_Core_Security('UTF-8');\n\t\t$this->input = new CI_Input($security);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function tear_down()\n\t{\n\t\t$_POST = [];\n\t\t$_GET = [];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_get_not_exists()\n\t{\n\t\t$this->assertSame(array(), $this->input->get());\n\t\t$this->assertNull($this->input->get('foo'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_get_exist()\n\t{\n\t\t$_SERVER['REQUEST_METHOD'] = 'GET';\n\t\t$_GET['foo'] = 'bar';\n\n\t\t$this->assertArrayHasKey('foo', $this->input->get());\n\t\t$this->assertEquals('bar', $this->input->get('foo'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_get_exist_with_xss_clean()\n\t{\n\t\t$_SERVER['REQUEST_METHOD'] = 'GET';\n\t\t$_GET['harm'] = \"Hello, i try to <script>alert('Hack');</script> your site\";\n\n\t\t$this->assertArrayHasKey('harm', $this->input->get());\n\t\t$this->assertEquals(\"Hello, i try to <script>alert('Hack');</script> your site\", $this->input->get('harm'));\n\t\t$this->assertEquals(\"Hello, i try to [removed]alert&#40;'Hack'&#41;;[removed] your site\", $this->input->get('harm', TRUE));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_post_not_exists()\n\t{\n\t\t$this->assertSame(array(), $this->input->post());\n\t\t$this->assertNull($this->input->post('foo'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_post_exist()\n\t{\n\t\t$_SERVER['REQUEST_METHOD'] = 'POST';\n\t\t$_POST['foo'] = 'bar';\n\n\t\t$this->assertArrayHasKey('foo', $this->input->post());\n\t\t$this->assertEquals('bar', $this->input->post('foo'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_post_exist_with_xss_clean()\n\t{\n\t\t$_SERVER['REQUEST_METHOD'] = 'POST';\n\t\t$_POST['harm'] = \"Hello, i try to <script>alert('Hack');</script> your site\";\n\n\t\t$this->assertArrayHasKey('harm', $this->input->post());\n\t\t$this->assertEquals(\"Hello, i try to <script>alert('Hack');</script> your site\", $this->input->post('harm'));\n\t\t$this->assertEquals(\"Hello, i try to [removed]alert&#40;'Hack'&#41;;[removed] your site\", $this->input->post('harm', TRUE));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_post_get()\n\t{\n\t\t$_SERVER['REQUEST_METHOD'] = 'POST';\n\t\t$_POST['foo'] = 'bar';\n\n\t\t$this->assertEquals('bar', $this->input->post_get('foo'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_post_get_array_notation()\n\t{\n\t\t$_SERVER['REQUEST_METHOD'] = 'POST';\n\t\t$_POST['foo'] = array('bar' => 'baz');\n\t\t$barArray = array('bar' => 'baz');\n\n\t\t$this->assertEquals('baz', $this->input->get_post('foo[bar]'));\n\t\t$this->assertEquals($barArray, $this->input->get_post('foo[]'));\n\t\t$this->assertNull($this->input->get_post('foo[baz]'));\n\n\t\t$this->assertEquals('baz', $this->input->post_get('foo[bar]'));\n\t\t$this->assertEquals($barArray, $this->input->post_get('foo[]'));\n\t\t$this->assertNull($this->input->post_get('foo[baz]'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_get_post()\n\t{\n\t\t$_SERVER['REQUEST_METHOD'] = 'GET';\n\t\t$_GET['foo'] = 'bar';\n\n\t\t$this->assertEquals('bar', $this->input->get_post('foo'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_get_post_array_notation()\n\t{\n\t\t$_SERVER['REQUEST_METHOD'] = 'GET';\n\t\t$_GET['foo'] = array('bar' => 'baz');\n\t\t$barArray = array('bar' => 'baz');\n\n\t\t$this->assertEquals('baz', $this->input->get_post('foo[bar]'));\n\t\t$this->assertEquals($barArray, $this->input->get_post('foo[]'));\n\t\t$this->assertNull($this->input->get_post('foo[baz]'));\n\n\t\t$this->assertEquals('baz', $this->input->post_get('foo[bar]'));\n\t\t$this->assertEquals($barArray, $this->input->post_get('foo[]'));\n\t\t$this->assertNull($this->input->post_get('foo[baz]'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_cookie()\n\t{\n\t\t$_COOKIE['foo'] = 'bar';\n\t\t$this->assertEquals('bar', $this->input->cookie('foo'));\n\t\t$this->assertNull($this->input->cookie('bar'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_server()\n\t{\n\t\t$this->assertEquals('GET', $this->input->server('REQUEST_METHOD'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_fetch_from_array()\n\t{\n\t\t$reflection = new ReflectionMethod($this->input, '_fetch_from_array');\n\t\t$reflection->setAccessible(TRUE);\n\n\t\t$data = array(\n\t\t\t'foo' => 'bar',\n\t\t\t'harm' => 'Hello, i try to <script>alert(\\'Hack\\');</script> your site',\n\t\t);\n\n\t\t$foo      = $reflection->invokeArgs($this->input, [&$data, 'foo']);\n\t\t$harm     = $reflection->invokeArgs($this->input, [&$data, 'harm']);\n\t\t$harmless = $reflection->invokeArgs($this->input, [&$data, 'harm', TRUE]);\n\n\t\t$this->assertEquals('bar', $foo);\n\t\t$this->assertEquals(\"Hello, i try to <script>alert('Hack');</script> your site\", $harm);\n\t\t$this->assertEquals(\"Hello, i try to [removed]alert&#40;'Hack'&#41;;[removed] your site\", $harmless);\n\n\t\t$_SERVER['REQUEST_METHOD'] = 'POST';\n\t\t$_POST['foo'] = array('bar' => 'baz');\n\t\t$barArray = array('bar' => 'baz');\n\n\t\t$this->assertEquals('baz', $this->input->post('foo[bar]'));\n\t\t$this->assertEquals($barArray, $this->input->post('foo[]'));\n\t\t$this->assertNull($this->input->post('foo[baz]'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_valid_ip()\n\t{\n\t\t$this->assertTrue($this->input->valid_ip('192.18.0.1'));\n\t\t$this->assertTrue($this->input->valid_ip('192.18.0.1', 'ipv4'));\n\t\t$this->assertFalse($this->input->valid_ip('555.0.0.0'));\n\t\t$this->assertFalse($this->input->valid_ip('2001:db8:0:85a3::ac1f:8001', 'ipv4'));\n\n\t\t// v6 tests\n\t\t$this->assertFalse($this->input->valid_ip('192.18.0.1', 'ipv6'));\n\n\t\t$ip_v6 = array(\n\t\t\t'2001:0db8:0000:85a3:0000:0000:ac1f:8001',\n\t\t\t'2001:db8:0:85a3:0:0:ac1f:8001',\n\t\t\t'2001:db8:0:85a3::ac1f:8001'\n\t\t);\n\n\t\tforeach ($ip_v6 as $ip)\n\t\t{\n\t\t\t$this->assertTrue($this->input->valid_ip($ip));\n\t\t\t$this->assertTrue($this->input->valid_ip($ip, 'ipv6'));\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_method()\n\t{\n\t\t$_SERVER['REQUEST_METHOD'] = 'GET';\n\t\t$this->assertEquals('get', $this->input->method());\n\t\t$this->assertEquals('GET', $this->input->method(TRUE));\n\t\t$_SERVER['REQUEST_METHOD'] = 'POST';\n\t\t$this->assertEquals('post', $this->input->method());\n\t\t$this->assertEquals('POST', $this->input->method(TRUE));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_is_ajax_request()\n\t{\n\t\t$this->assertFalse($this->input->is_ajax_request());\n\t\t$_SERVER['HTTP_X_REQUESTED_WITH'] = 'test';\n\t\t$this->assertFalse($this->input->is_ajax_request());\n\t\t$_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest';\n\t\t$this->assertTrue($this->input->is_ajax_request());\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_input_stream()\n\t{\n\t\t$this->markTestSkipped('TODO: Find a way to test input://');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_set_cookie()\n\t{\n\t\t$this->markTestSkipped('TODO: Find a way to test HTTP headers');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_get_request_header()\n\t{\n\t\t$this->markTestSkipped('TODO: Find a way to test HTTP headers');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_ip_address()\n\t{\n\t\t$reflection = new ReflectionProperty($this->input, 'ip_address');\n\t\t$reflection->setAccessible(TRUE);\n\n\t\t$reflection->setValue($this->input, '127.0.0.1');\n\t\t$this->assertEquals('127.0.0.1', $this->input->ip_address());\n\n\t\t// 127.0.0.1 is set in our Bootstrap file\n\t\t$reflection->setValue($this->input, FALSE);\n\t\t$this->assertEquals('127.0.0.1', $this->input->ip_address());\n\n\t\t// Invalid\n\t\t$_SERVER['REMOTE_ADDR'] = 'invalid_ip_address';\n\t\t$reflection->setValue($this->input, FALSE); // reset cached value\n\t\t$this->assertEquals('0.0.0.0', $this->input->ip_address());\n\n\t\t$_SERVER['REMOTE_ADDR'] = '127.0.0.1';\n\n\t\t// Proxy_ips tests\n\t\t$reflection->setValue($this->input, FALSE);\n\t\t$this->ci_set_config('proxy_ips', '127.0.0.3, 127.0.0.4, 127.0.0.2');\n\t\t$_SERVER['HTTP_CLIENT_IP'] = '127.0.0.2';\n\t\t$this->assertEquals('127.0.0.1', $this->input->ip_address());\n\n\t\t// Invalid spoof\n\t\t$reflection->setValue($this->input, FALSE);\n\t\t$this->ci_set_config('proxy_ips', 'invalid_ip_address');\n\t\t$_SERVER['HTTP_CLIENT_IP'] = 'invalid_ip_address';\n\t\t$this->assertEquals('127.0.0.1', $this->input->ip_address());\n\n\t\t$reflection->setValue($this->input, FALSE);\n\t\t$this->ci_set_config('proxy_ips', 'http://foo/bar/baz, 127.0.0.1/1');\n\t\t$_SERVER['HTTP_CLIENT_IP'] = '127.0.0.1';\n\t\t$this->assertEquals('127.0.0.1', $this->input->ip_address());\n\n\t\t$reflection->setValue($this->input, FALSE);\n\t\t$this->ci_set_config('proxy_ips', 'http://foo/bar/baz, 127.0.0.2');\n\t\t$_SERVER['HTTP_CLIENT_IP'] = '127.0.0.2';\n\t\t$_SERVER['REMOTE_ADDR'] = '127.0.0.2';\n\t\t$this->assertEquals('127.0.0.2', $this->input->ip_address());\n\n\t\t// IPv6\n\t\t$reflection->setValue($this->input, FALSE);\n\t\t$this->ci_set_config('proxy_ips', 'FE80:0000:0000:0000:0202:B3FF:FE1E:8329/1, FE80:0000:0000:0000:0202:B3FF:FE1E:8300/2');\n\t\t$_SERVER['HTTP_CLIENT_IP'] = 'FE80:0000:0000:0000:0202:B3FF:FE1E:8300';\n\t\t$_SERVER['REMOTE_ADDR'] = 'FE80:0000:0000:0000:0202:B3FF:FE1E:8329';\n\t\t$this->assertEquals('FE80:0000:0000:0000:0202:B3FF:FE1E:8300', $this->input->ip_address());\n\n\t\t$reflection->setValue($this->input, FALSE);\n\t\t$this->ci_set_config('proxy_ips', '0::/32');\n\t\t$_SERVER['HTTP_CLIENT_IP'] = '127.0.0.7';\n\t\t$_SERVER['REMOTE_ADDR'] = '0000:0000:0000:0000:0000:0000:0000:0001';\n\t\t$this->assertEquals('127.0.0.7', $this->input->ip_address());\n\n\t\t$reflection->setValue($this->input, FALSE);\n\t\t$_SERVER['REMOTE_ADDR'] = '127.0.0.1'; // back to reality\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_user_agent()\n\t{\n\t\t$_SERVER['HTTP_USER_AGENT'] = 'test';\n\t\t$this->assertEquals('test', $this->input->user_agent());\n\t}\n}\n"
  },
  {
    "path": "tests/codeigniter/core/Lang_test.php",
    "content": "<?php\n\nclass Lang_test extends CI_TestCase {\n\n\tprotected $lang;\n\n\tpublic function set_up()\n\t{\n\t\t$loader_cls = $this->ci_core_class('load');\n\t\t$this->ci_instance_var('load', new $loader_cls);\n\t\t$cls = $this->ci_core_class('lang');\n\t\t$this->lang = new $cls;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_load()\n\t{\n\t\t// Regular usage\n\t\t$this->ci_vfs_clone('system/language/english/profiler_lang.php');\n\t\t$this->assertTrue($this->lang->load('profiler', 'english'));\n\t\t$this->assertEquals('URI STRING', $this->lang->language['profiler_uri_string']);\n\n\t\t// Already loaded file\n\t\t$this->assertNull($this->lang->load('profiler', 'english'));\n\n\t\t// Unspecified language (defaults to english)\n\t\t$this->ci_vfs_clone('system/language/english/date_lang.php');\n\t\t$this->assertTrue($this->lang->load('date'));\n\t\t$this->assertEquals('Year', $this->lang->language['date_year']);\n\n\t\t// A language other than english\n\t\t$this->ci_vfs_clone('system/language/english/email_lang.php', 'system/language/german/');\n\t\t$this->assertTrue($this->lang->load('email', 'german'));\n\t\t$this->assertEquals('german', $this->lang->is_loaded['email_lang.php']);\n\n\t\t// Non-existent file\n\t\t$this->setExpectedException(\n\t\t\t'RuntimeException',\n\t\t\t'CI Error: Unable to load the requested language file: language/english/nonexistent_lang.php'\n\t\t);\n\t\t$this->lang->load('nonexistent');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_non_alpha_idiom()\n\t{\n\t\t// Non-alpha idiom (should act the same as unspecified language)\n\t\t// test with existing file\n\t\t$this->ci_vfs_clone('system/language/english/number_lang.php');\n\t\t$this->ci_vfs_clone('system/language/english/number_lang.php', 'system/language/123funny/');\n\t\t$this->assertTrue($this->lang->load('number', '123funny'));\n\t\t$this->assertEquals('Bytes', $this->lang->language['bytes']);\n\n\t\t// test without existing file\n\t\t$this->ci_vfs_clone('system/language/english/email_lang.php');\n\t\t$this->assertTrue($this->lang->load('email', '456funny'));\n\t\t$this->assertEquals('You did not specify a SMTP hostname.', $this->lang->language['email_no_hostname']);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_multiple_file_load()\n\t{\n\t\t// Multiple files\n\t\t$this->ci_vfs_clone('system/language/english/profiler_lang.php');\n\t\t$files = array(\n\t\t\t0 => 'profiler',\n\t\t\t1 => 'nonexistent'\n\t\t);\n\t\t$this->setExpectedException(\n\t\t\t'RuntimeException',\n\t\t\t'CI Error: Unable to load the requested language file: language/english/nonexistent_lang.php'\n\t\t);\n\t\t$this->lang->load($files, 'english');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_alternative_path_load()\n\t{\n\t\t// Alternative Path\n\t\t$this->ci_vfs_clone('system/language/english/profiler_lang.php');\n\t\t$this->assertTrue($this->lang->load('profiler', 'english', FALSE, TRUE, 'vfs://system/'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * @depends\ttest_load\n\t */\n\tpublic function test_line()\n\t{\n\t\t$this->ci_vfs_clone('system/language/english/profiler_lang.php');\n\t\t$this->lang->load('profiler', 'english');\n\t\t$this->assertEquals('URI STRING', $this->lang->line('profiler_uri_string'));\n\t\t$this->assertFalse($this->lang->line('nonexistent_string'));\n\t\t$this->assertFalse($this->lang->line(NULL));\n\t}\n}\n"
  },
  {
    "path": "tests/codeigniter/core/Loader_test.php",
    "content": "<?php\n\nclass Loader_test extends CI_TestCase {\n\n\tprivate $ci_obj;\n\n\tpublic function set_up()\n\t{\n\t\t// Instantiate a new loader\n\t\t$loader = $this->ci_core_class('loader');\n\t\t$this->load = new $loader();\n\n\t\t// Get CI instance\n\t\t$this->ci_obj = $this->ci_instance();\n\n\t\t// Set subclass prefix\n\t\t$this->prefix = 'MY_';\n\t\t$this->ci_set_config('subclass_prefix', $this->prefix);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_library()\n\t{\n\t\t// Test getting CI_Loader object\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->library(NULL));\n\n\t\t// Create library in VFS\n\t\t$lib = 'unit_test_lib';\n\t\t$class = 'CI_'.ucfirst($lib);\n\t\t$this->ci_vfs_create(ucfirst($lib), '<?php class '.$class.' { }', $this->ci_base_root, 'libraries');\n\n\t\t// Test is_loaded fail\n\t\t$this->assertFalse($this->load->is_loaded(ucfirst($lib)));\n\n\t\t// Test loading as an array.\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->library(array($lib)));\n\t\t$this->assertTrue(class_exists($class), $class.' does not exist');\n\t\t$this->assertObjectHasAttribute($lib, $this->ci_obj);\n\t\t$this->assertInstanceOf($class, $this->ci_obj->$lib);\n\n\t\t// Create library in VFS\n\t\t$lib = array('unit_test_lib' => 'unit_test_lib');\n\n\t\t// Test loading as an array (int).\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->library($lib));\n\t\t$this->assertTrue(class_exists($class), $class.' does not exist');\n\n\t\t// Test a string given to params\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->library($lib, ' '));\n\n\t\t// test non existent lib\n\t\t$lib = 'non_existent_test_lib';\n\n\t\t$this->setExpectedException(\n\t\t\t'RuntimeException',\n\t\t\t'CI Error: Unable to load the requested class: '.ucfirst($lib)\n\t\t);\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->library($lib));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_bad_library()\n\t{\n\t\t$lib = 'bad_test_lib';\n\t\t$this->ci_vfs_create(ucfirst($lib), '', $this->ci_app_root, 'libraries');\n\t\t$this->setExpectedException(\n\t\t\t'RuntimeException',\n\t\t\t'CI Error: Non-existent class: '.ucfirst($lib)\n\t\t);\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->library($lib));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_library_extension()\n\t{\n\t\t// Create library and extension in VFS\n\t\t$name = 'ext_test_lib';\n\t\t$lib = ucfirst($name);\n\t\t$class = 'CI_'.$lib;\n\t\t$ext = $this->prefix.$lib;\n\t\t$this->ci_vfs_create($lib, '<?php class '.$class.' { }', $this->ci_base_root, 'libraries');\n\t\t$this->ci_vfs_create($ext, '<?php class '.$ext.' extends '.$class.' { }', $this->ci_app_root, 'libraries');\n\n\t\t// Test loading with extension\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->library($lib));\n\t\t$this->assertTrue(class_exists($class), $class.' does not exist');\n\t\t$this->assertTrue(class_exists($ext), $ext.' does not exist');\n\t\t$this->assertObjectHasAttribute($name, $this->ci_obj);\n\t\t$this->assertInstanceOf($class, $this->ci_obj->$name);\n\t\t$this->assertInstanceOf($ext, $this->ci_obj->$name);\n\n\t\t// Test reloading with object name\n\t\t$obj = 'exttest';\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->library($lib, NULL, $obj));\n\t\t$this->assertObjectHasAttribute($obj, $this->ci_obj);\n\t\t$this->assertInstanceOf($class, $this->ci_obj->$obj);\n\t\t$this->assertInstanceOf($ext, $this->ci_obj->$obj);\n\n\t\t// Test reloading\n\t\tunset($this->ci_obj->$name);\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->library($lib));\n\t\t$this->assertObjectHasAttribute($name, $this->ci_obj);\n\n\t\t// Create baseless library\n\t\t$name = 'ext_baseless_lib';\n\t\t$lib = ucfirst($name);\n\t\t$class = $this->prefix.$lib;\n\t\t$this->ci_vfs_create($class, '<?php class '.$class.' { }', $this->ci_app_root, 'libraries');\n\n\t\t// Test missing base class\n\t\t$this->setExpectedException(\n\t\t\t'RuntimeException',\n\t\t\t'CI Error: Unable to load the requested class: '.$lib\n\t\t);\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->library($lib));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_library_config()\n\t{\n\t\t// Create library in VFS\n\t\t$lib = 'unit_test_config_lib';\n\t\t$class = 'CI_'.ucfirst($lib);\n\t\t$content = '<?php class '.$class.' { public function __construct($params) { $this->config = $params; } }';\n\t\t$this->ci_vfs_create(ucfirst($lib), $content, $this->ci_base_root, 'libraries');\n\n\t\t// Create config file\n\t\t$cfg = array(\n\t\t\t'foo' => 'bar',\n\t\t\t'bar' => 'baz',\n\t\t\t'baz' => false\n\t\t);\n\t\t$this->ci_vfs_create($lib, '<?php $config = '.var_export($cfg, TRUE).';', $this->ci_app_root, 'config');\n\n\t\t// Test object name and config\n\t\t$obj = 'testy';\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->library($lib, NULL, $obj));\n\t\t$this->assertTrue(class_exists($class), $class.' does not exist');\n\t\t$this->assertObjectHasAttribute($obj, $this->ci_obj);\n\t\t$this->assertInstanceOf($class, $this->ci_obj->$obj);\n\t\t$this->assertEquals($cfg, $this->ci_obj->$obj->config);\n\n\t\t// Test is_loaded\n\t\t$this->assertEquals($obj, $this->load->is_loaded(ucfirst($lib)));\n\n\t\t// Test to load another class with the same object name\n\t\t$lib = 'another_test_lib';\n\t\t$class = ucfirst($lib);\n\t\t$this->ci_vfs_create(ucfirst($lib), '<?php class '.$class.' { }', $this->ci_app_root, 'libraries');\n\t\t$this->setExpectedException(\n\t\t\t'RuntimeException',\n\t\t\t\"CI Error: Resource '\".$obj.\"' already exists and is not a \".$class.\" instance.\"\n\t\t);\n\t\t$this->load->library($lib, NULL, $obj);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_load_library_in_application_dir()\n\t{\n\t\t// Create library in VFS\n\t\t$lib = 'super_test_library';\n\t\t$class = ucfirst($lib);\n\t\t$this->ci_vfs_create(ucfirst($lib), '<?php class '.$class.' { }', $this->ci_app_root, 'libraries');\n\n\t\t// Load library\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->library($lib));\n\n\t\t// Was the model class instantiated.\n\t\t$this->assertTrue(class_exists($class), $class.' does not exist');\n\t\t$this->assertObjectHasAttribute($lib, $this->ci_obj);\n\t\t$this->assertInstanceOf($class, $this->ci_obj->$lib);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_driver()\n\t{\n\t\t// Call the autoloader, to include system/libraries/Driver.php\n\t\tclass_exists('CI_Driver_Library', TRUE);\n\n\t\t// Create driver in VFS\n\t\t$driver = 'unit_test_driver';\n\t\t$dir = ucfirst($driver);\n\t\t$class = 'CI_'.$dir;\n\t\t$content = '<?php class '.$class.' { } ';\n\t\t$this->ci_vfs_create(ucfirst($driver), $content, $this->ci_base_root, 'libraries/'.$dir);\n\n\t\t// Test loading as an array.\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->driver(array($driver)));\n\t\t$this->assertTrue(class_exists($class), $class.' does not exist');\n\t\t$this->assertObjectHasAttribute($driver, $this->ci_obj);\n\t\t$this->assertInstanceOf($class, $this->ci_obj->$driver);\n\n\t\t// Test loading as a library with a name\n\t\t$obj = 'testdrive';\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->library($driver, NULL, $obj));\n\t\t$this->assertObjectHasAttribute($obj, $this->ci_obj);\n\t\t$this->assertInstanceOf($class, $this->ci_obj->$obj);\n\n\t\t// Test a string given to params\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->driver($driver, ' '));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_models()\n\t{\n\t\t$this->ci_set_core_class('model', 'CI_Model');\n\n\t\t// Create model in VFS\n\t\t$model = 'Unit_test_model';\n\t\t$content = '<?php class '.$model.' extends CI_Model {} ';\n\t\t$this->ci_vfs_create($model, $content, $this->ci_app_root, 'models');\n\n\t\t// Load model\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->model($model));\n\n\t\t// Was the model class instantiated.\n\t\t$this->assertTrue(class_exists($model));\n\t\t$this->assertObjectHasAttribute($model, $this->ci_obj);\n\n\t\t// Test no model given\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->model(''));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_model_subdir()\n\t{\n\t\t// Make sure base class is loaded - we'll test _ci_include later\n\t\t$this->ci_core_class('model');\n\n\t\t// Create modelin VFS\n\t\t$model = 'Test_sub_model';\n\t\t$base = 'CI_Model';\n\t\t$subdir = 'cars';\n\t\t$this->ci_vfs_create($model, '<?php class '.$model.' extends '.$base.' { }', $this->ci_app_root,\n\t\t\tarray('models', $subdir));\n\n\t\t// Load model\n\t\t$name = 'testors';\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->model($subdir.'/'.$model, $name));\n\n\t\t// Was the model class instantiated?\n\t\t$this->assertTrue(class_exists($model));\n\t\t$this->assertObjectHasAttribute($name, $this->ci_obj);\n\t\t$this->assertObjectHasAttribute($name, $this->ci_obj);\n\t\t$this->assertInstanceOf($base, $this->ci_obj->$name);\n\t\t$this->assertInstanceOf($model, $this->ci_obj->$name);\n\n\t\t// Test name conflict\n\t\t$obj = 'conflict';\n\t\t$this->ci_obj->$obj = new stdClass();\n\t\t$this->setExpectedException(\n\t\t\t'RuntimeException',\n\t\t\t'The model name you are loading is the name of a resource that is already being used: '.$obj\n\t\t);\n\t\t$this->load->model('not_real', $obj);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_non_existent_model()\n\t{\n\t\t$this->setExpectedException(\n\t\t\t'RuntimeException',\n\t\t\t'Unable to locate the model you have specified: Ci_test_nonexistent_model.php'\n\t\t);\n\n\t\t$this->load->model('ci_test_nonexistent_model.php');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_invalid_model()\n\t{\n\t\t$this->ci_set_core_class('model', 'CI_Model');\n\n\t\t// Create model in VFS\n\t\t$model = 'Unit_test_invalid_model';\n\t\t$content = '<?php class '.$model.' {} ';\n\t\t$this->ci_vfs_create($model, $content, $this->ci_app_root, 'models');\n\n\t\t// Test no extending\n\t\t$this->setExpectedException(\n\t\t\t'RuntimeException',\n\t\t\t'Class '.$model.' doesn\\'t extend CI_Model'\n\t\t);\n\t\t$this->load->model($model);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t// public function testDatabase()\n\t// {\n\t// \t$this->assertInstanceOf('CI_Loader', $this->load->database());\n\t// \t$this->assertInstanceOf('CI_Loader', $this->load->dbutil());\n\t// }\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_load_view()\n\t{\n\t\t// Create view in VFS\n\t\t$view = 'unit_test_view';\n\t\t$var = 'hello';\n\t\t$value = 'World!';\n\t\t$content = 'This is my test page.  ';\n\t\t$this->ci_vfs_create($view, $content.'<?php echo (isset($'.$var.') ? $'.$var.' : \"undefined\");', $this->ci_app_root, 'views');\n\n\t\t// Test returning view\n\t\t$out = $this->load->view($view, array($var => $value), TRUE);\n\t\t$this->assertEquals($content.$value, $out);\n\n\t\t// Test view with missing parameter in $vars\n\t\t$out = $this->load->view($view, [], TRUE);\n\t\t$this->assertEquals($content.'undefined', $out);\n\n\t\t// Mock output class\n\t\t$output = $this->getMockBuilder('CI_Output')->setMethods(array('append_output'))->getMock();\n\t\t$output->expects($this->once())->method('append_output')->with($content.$value);\n\t\t$this->ci_instance_var('output', $output);\n\n\t\t// Test view output and $vars as an object\n\t\t$vars = new stdClass();\n\t\t$vars->$var = $value;\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->view($view, $vars));\n\n\t\t// Create another view in VFS, nesting the first one without its own $vars\n\t\t$nesting_view = 'unit_test_nesting_view';\n\t\t$nesting_content = 'Here comes a nested view.  ';\n\t\t$this->ci_vfs_create($nesting_view, $nesting_content.'<?php $loader->view(\"'.$view.'\");', $this->ci_app_root, 'views');\n\n\t\t// Test $vars inheritance to nested views\n\t\t$out = $this->load->view($nesting_view, array(\"loader\" => $this->load, $var => $value), TRUE);\n\t\t$this->assertEquals($nesting_content.$content.$value, $out);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_non_existent_view()\n\t{\n\t\t$this->setExpectedException(\n\t\t\t'RuntimeException',\n\t\t\t'CI Error: Unable to load the requested file: ci_test_nonexistent_view.php'\n\t\t);\n\n\t\t$this->load->view('ci_test_nonexistent_view', array('foo' => 'bar'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_file()\n\t{\n\t\t// Create view in VFS\n\t\t$dir = 'views';\n\t\t$file = 'ci_test_mock_file';\n\t\t$content = 'Here is a test file, which we will load now.';\n\t\t$this->ci_vfs_create($file, $content, $this->ci_app_root, $dir);\n\n\t\t// Just like load->view(), take the output class out of the mix here.\n\t\t$out = $this->load->file(APPPATH.$dir.'/'.$file.'.php', TRUE);\n\t\t$this->assertEquals($content, $out);\n\n\t\t// Test non-existent file\n\t\t$this->setExpectedException(\n\t\t\t'RuntimeException',\n\t\t\t'CI Error: Unable to load the requested file: ci_test_file_not_exists'\n\t\t);\n\n\t\t$this->load->file('ci_test_file_not_exists', TRUE);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_vars()\n\t{\n\t\t$key1 = 'foo';\n\t\t$val1 = 'bar';\n\t\t$key2 = 'boo';\n\t\t$val2 = 'hoo';\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->vars(array($key1 => $val1)));\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->vars($key2, $val2));\n\t\t$this->assertEquals($val1, $this->load->get_var($key1));\n\t\t$this->assertEquals(array($key1 => $val1, $key2 => $val2), $this->load->get_vars());\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_clear_vars()\n\t{\n\t\t$key1 = 'foo';\n\t\t$val1 = 'bar';\n\t\t$key2 = 'boo';\n\t\t$val2 = 'hoo';\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->vars(array($key1 => $val1)));\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->vars($key2, $val2));\n\t\t$this->assertEquals($val1, $this->load->get_var($key1));\n\t\t$this->assertEquals(array($key1 => $val1, $key2 => $val2), $this->load->get_vars());\n\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->clear_vars());\n\t\t$this->assertEquals('', $this->load->get_var($key1));\n\t\t$this->assertEquals('', $this->load->get_var($key2));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_helper()\n\t{\n\t\t// Create helper in VFS\n\t\t$helper = 'test';\n\t\t$func = '_my_helper_test_func';\n\t\t$content = '<?php function '.$func.'() { return TRUE; } ';\n\t\t$this->ci_vfs_create($helper.'_helper', $content, $this->ci_base_root, 'helpers');\n\n\t\t// Create helper extension\n\t\t$exfunc = '_my_extension_func';\n\t\t$content = '<?php function '.$exfunc.'() { return TRUE; } ';\n\t\t$this->ci_vfs_create($this->prefix.$helper.'_helper', $content, $this->ci_app_root, 'helpers');\n\n\t\t// Load helper\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->helper($helper));\n\t\t$this->assertTrue(function_exists($func), $func.' does not exist');\n\t\t$this->assertTrue(function_exists($exfunc), $exfunc.' does not exist');\n\n\t\t// Create baseless extension\n\t\t$ext = 'bad_ext';\n\t\t$this->ci_vfs_create($this->prefix.$ext.'_helper', '', $this->ci_app_root, 'helpers');\n\n\t\t// Test bad extension\n\t\t$this->setExpectedException(\n\t\t\t'RuntimeException',\n\t\t\t'CI Error: Unable to load the requested file: helpers/'.$ext.'_helper.php'\n\t\t);\n\t\t$this->load->helper($ext);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_non_existent_helper()\n\t{\n\t\t$this->setExpectedException(\n\t\t\t'RuntimeException',\n\t\t\t'CI Error: Unable to load the requested file: helpers/bad_helper.php'\n\t\t);\n\t\t$this->load->helper('bad');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_loading_multiple_helpers()\n\t{\n\t\t// Create helpers in VFS\n\t\t$helpers = array();\n\t\t$funcs = array();\n\t\t$files = array();\n\t\tfor ($i = 1; $i <= 3; ++$i) {\n\t\t\t$helper = 'test'.$i;\n\t\t\t$helpers[] = $helper;\n\t\t\t$func = '_my_helper_test_func'.$i;\n\t\t\t$funcs[] = $func;\n\t\t\t$files[$helper.'_helper'] = '<?php function '.$func.'() { return TRUE; } ';\n\t\t}\n\t\t$this->ci_vfs_create($files, NULL, $this->ci_base_root, 'helpers');\n\n\t\t// Load helpers\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->helpers($helpers));\n\n\t\t// Verify helper existence\n\t\tforeach ($funcs as $func) {\n\t\t\t$this->assertTrue(function_exists($func), $func.' does not exist');\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_language()\n\t{\n\t\t// Mock lang class and test load call\n\t\t$file = 'test';\n\t\t$lang = $this->getMockBuilder('CI_Lang')->setMethods(array('load'))->getMock();\n\t\t$lang->expects($this->once())->method('load')->with($file);\n\t\t$this->ci_instance_var('lang', $lang);\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->language($file));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_packages()\n\t{\n\t\t// Create model in VFS package path\n\t\t$dir = 'third-party';\n\t\t$lib = 'unit_test_package';\n\t\t$class = ucfirst($lib);\n\t\t$this->ci_vfs_create(ucfirst($lib), '<?php class '.$class.' { }', $this->ci_app_root, array($dir, 'libraries'));\n\n\t\t// Get paths\n\t\t$paths = $this->load->get_package_paths(TRUE);\n\n\t\t// Test failed load without path\n\t\t$this->setExpectedException(\n\t\t\t'RuntimeException',\n\t\t\t'CI Error: Unable to load the requested class: '.ucfirst($lib)\n\t\t);\n\t\t$this->load->library($lib);\n\n\t\t// Add path and verify\n\t\t$path = APPPATH.$dir.'/';\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->add_package_path($path));\n\t\t$this->assertContains($path, $this->load->get_package_paths(TRUE));\n\n\t\t// Test successful load\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->library($lib));\n\t\t$this->assertTrue(class_exists($class), $class.' does not exist');\n\n\t\t// Add another path\n\t\t$path2 = APPPATH.'another/';\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->add_package_path($path2));\n\t\t$this->assertContains($path2, $this->load->get_package_paths(TRUE));\n\n\t\t// Remove last path\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->remove_package_path());\n\t\t$this->assertNotContains($path2, $this->load->get_package_paths(TRUE));\n\n\t\t// Remove path and verify restored paths\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->remove_package_path($path));\n\t\t$this->assertEquals($paths, $this->load->get_package_paths(TRUE));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_remove_package_path()\n\t{\n\t\t$dir = 'third-party';\n\t\t$path = APPPATH.$dir.'/';\n\t\t$path2 = APPPATH.'another/';\n\t\t$paths = $this->load->get_package_paths(TRUE);\n\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->add_package_path($path));\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->remove_package_path($path));\n\t\t$this->assertEquals($paths, $this->load->get_package_paths(TRUE));\n\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->add_package_path($path2));\n\t\t$this->assertInstanceOf('CI_Loader', $this->load->remove_package_path());\n\t\t$this->assertNotContains($path2, $this->load->get_package_paths(TRUE));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_load_config()\n\t{\n\t\t$cfg = 'someconfig';\n\t\t$this->assertTrue($this->load->config($cfg, FALSE));\n\t\t$this->assertContains($cfg, $this->ci_obj->config->loaded);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_initialize()\n\t{\n\t\t// Create helper in VFS\n\t\t$helper = 'autohelp';\n\t\t$hlp_func = '_autohelp_test_func';\n\t\t$content = '<?php function '.$hlp_func.'() { return TRUE; }';\n\t\t$this->ci_vfs_create($helper.'_helper', $content, $this->ci_app_root, 'helpers');\n\n\t\t// Create library in VFS\n\t\t$lib = 'autolib';\n\t\t$lib_class = 'CI_'.ucfirst($lib);\n\t\t$this->ci_vfs_create(ucfirst($lib), '<?php class '.$lib_class.' { }', $this->ci_base_root, 'libraries');\n\n\t\t// Create driver in VFS\n\t\t$drv = 'autodrv';\n\t\t$subdir = ucfirst($drv);\n\t\t$drv_class = 'CI_'.$subdir;\n\t\t$this->ci_vfs_create(ucfirst($drv), '<?php class '.$drv_class.' { }', $this->ci_base_root, array('libraries', $subdir));\n\n\t\t// Create model in VFS package path\n\t\t$dir = 'testdir';\n\t\t$path = APPPATH.$dir.'/';\n\t\t$model = 'Automod';\n\t\t$this->ci_vfs_create($model, '<?php class '.$model.' extends CI_Model { }', $this->ci_app_root, array($dir, 'models'));\n\n\t\t// Create autoloader config\n\t\t$cfg = array(\n\t\t\t'packages' => array($path),\n\t\t\t'helper' => array($helper),\n\t\t\t'libraries' => array($lib),\n\t\t\t'drivers' => array($drv),\n\t\t\t'model' => array($model),\n\t\t\t'config' => array('config1', 'config2')\n\t\t);\n\t\t$this->ci_vfs_create('autoload', '<?php $autoload = '.var_export($cfg, TRUE).';', $this->ci_app_root, 'config');\n\n\t\t$this->load->initialize();\n\n\t\t// Verify path\n\t\t$this->assertContains($path, $this->load->get_package_paths());\n\n\t\t// Verify helper\n\t\t$this->assertTrue(function_exists($hlp_func), $hlp_func.' does not exist');\n\n\t\t// Verify library\n\t\t$this->assertTrue(class_exists($lib_class), $lib_class.' does not exist');\n\t\t$this->assertObjectHasAttribute($lib, $this->ci_obj);\n\t\t$this->assertInstanceOf($lib_class, $this->ci_obj->$lib);\n\n\t\t// Verify driver\n\t\t$this->assertTrue(class_exists($drv_class), $drv_class.' does not exist');\n\t\t$this->assertObjectHasAttribute($drv, $this->ci_obj);\n\t\t$this->assertInstanceOf($drv_class, $this->ci_obj->$drv);\n\n\t\t// Verify model\n\t\t$this->assertTrue(class_exists($model), $model.' does not exist');\n\t\t$this->assertObjectHasAttribute($model, $this->ci_obj);\n\t\t$this->assertInstanceOf($model, $this->ci_obj->$model);\n\n\t\t// Verify config calls\n\t\t$this->assertEquals($cfg['config'], $this->ci_obj->config->loaded);\n\t}\n}\n"
  },
  {
    "path": "tests/codeigniter/core/Log_test.php",
    "content": "<?php\nclass Log_test extends CI_TestCase {\n\n\tpublic function test_configuration()\n\t{\n\t\t$path       = new ReflectionProperty('CI_Log', '_log_path');\n\t\t$path->setAccessible(TRUE);\n\t\t$threshold  = new ReflectionProperty('CI_Log', '_threshold');\n\t\t$threshold->setAccessible(TRUE);\n\t\t$date_fmt   = new ReflectionProperty('CI_Log', '_date_fmt');\n\t\t$date_fmt->setAccessible(TRUE);\n\t\t$filename   = new ReflectionProperty('CI_Log', '_log_filename');\n\t\t$filename->setAccessible(TRUE);\n\t\t$file_perms = new ReflectionProperty('CI_Log', '_file_permissions');\n\t\t$file_perms->setAccessible(TRUE);\n\t\t$enabled    = new ReflectionProperty('CI_Log', '_enabled');\n\t\t$enabled->setAccessible(TRUE);\n\n\t\t$this->ci_set_config('log_path', $this->ci_readonly_dir->url());\n\t\t$this->ci_set_config('log_threshold', 'z');\n\t\t$this->ci_set_config('log_date_format', 'd.m.Y');\n\t\t$this->ci_set_config('log_filename', '');\n\t\t$this->ci_set_config('log_file_permissions', '');\n\t\t$instance = new CI_Log();\n\n\t\t$this->assertNotFalse(strpos($path->getValue($instance), 'application/readonly'));\n\t\t$this->assertEquals($threshold->getValue($instance), 1);\n\t\t$this->assertEquals($date_fmt->getValue($instance), 'd.m.Y');\n\t\t$this->assertEquals($filename->getValue($instance), 'log-'.date('Y-m-d').'.php');\n\t\t$this->assertEquals($file_perms->getValue($instance), 0644);\n\t\t$this->assertFalse($enabled->getValue($instance));\n\n\t\t$this->ci_set_config('log_path', '');\n\t\t$this->ci_set_config('log_threshold', '0');\n\t\t$this->ci_set_config('log_date_format', '');\n\t\t$this->ci_set_config('log_filename', 'testname.log');\n\t\t$this->ci_set_config('log_file_permissions', 0600);\n\t\t$instance = new CI_Log();\n\n\t\t$this->assertEquals($path->getValue($instance), $this->ci_vfs_root->url().'application/logs'.DIRECTORY_SEPARATOR);\n\t\t$this->assertEquals($threshold->getValue($instance), 0);\n\t\t$this->assertEquals($date_fmt->getValue($instance), 'Y-m-d H:i:s');\n\t\t$this->assertEquals($filename->getValue($instance), 'testname.log');\n\t\t$this->assertEquals($file_perms->getValue($instance), 0600);\n\t\t$this->assertEquals($enabled->getValue($instance), TRUE);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_format_line()\n\t{\n\t\t$this->ci_set_config('log_path', '');\n\t\t$this->ci_set_config('log_threshold', 0);\n\t\t$instance = new CI_Log();\n\n\t\t$format_line = new ReflectionMethod($instance, '_format_line');\n\t\t$format_line->setAccessible(TRUE);\n\t\t$this->assertEquals(\n\t\t\t$format_line->invoke($instance, 'LEVEL', 'Timestamp', 'Message'),\n\t\t\t\"LEVEL - Timestamp --> Message\".PHP_EOL\n\t\t);\n\t}\n}\n"
  },
  {
    "path": "tests/codeigniter/core/Model_test.php",
    "content": "<?php\n\nclass Model_test extends CI_TestCase {\n\n\tprivate $ci_obj;\n\n\tpublic function set_up()\n\t{\n\t\t$loader = $this->ci_core_class('loader');\n\t\t$this->load = new $loader();\n\t\t$this->ci_obj = $this->ci_instance();\n\t\t$this->ci_set_core_class('model', 'CI_Model');\n\n\t\t$model_code =<<<MODEL\n<?php\nclass Test_model extends CI_Model {\n\n\tpublic \\$property = 'foo';\n\n}\nMODEL;\n\n\t\t$this->ci_vfs_create('Test_model', $model_code, $this->ci_app_root, 'models');\n\t\t$this->load->model('test_model');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test__get()\n\t{\n\t\t$this->assertEquals('foo', $this->ci_obj->test_model->property);\n\n\t\t$this->ci_obj->controller_property = 'bar';\n\t\t$this->assertEquals('bar', $this->ci_obj->test_model->controller_property);\n\t}\n\n}"
  },
  {
    "path": "tests/codeigniter/core/Output_test.php",
    "content": "<?php\n\nclass Output_test extends CI_TestCase {\n\n\tpublic $output;\n\tprotected $_output_data = '';\n\n\tpublic function set_up()\n\t{\n\t\t$this->_output_data =<<<HTML\n\t\t<html lang=\"en\">\n\t\t\t<head>\n\t\t\t\t<title>Basic HTML</title>\n\t\t\t</head>\n\t\t\t<body>\n\t\t\t\tTest\n\t\t\t</body>\n\t\t</html>\nHTML;\n\t\t$this->ci_set_config('charset', 'UTF-8');\n\t\t$output = $this->ci_core_class('output');\n\t\t$this->output = new $output();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_set_get_append_output()\n\t{\n\t\t$append = \"<!-- comment /-->\\n\";\n\n\t\t$this->assertEquals(\n\t\t\t$this->_output_data.$append,\n\t\t\t$this->output\n\t\t\t\t->set_output($this->_output_data)\n\t\t\t\t->append_output(\"<!-- comment /-->\\n\")\n\t\t\t\t->get_output()\n\t\t);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_get_content_type()\n\t{\n\t\t$this->assertEquals('text/html', $this->output->get_content_type());\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_get_header()\n\t{\n\t\t$this->assertNull($this->output->get_header('Non-Existent-Header'));\n\n\t\t// TODO: Find a way to test header() values as well. Currently,\n\t\t//\t PHPUnit prevents this by not using output buffering.\n\n\t\t$this->output->set_content_type('text/plain', 'WINDOWS-1251');\n\t\t$this->assertEquals(\n\t\t\t'text/plain; charset=WINDOWS-1251',\n\t\t\t$this->output->get_header('content-type')\n\t\t);\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/core/Security_test.php",
    "content": "<?php\n\nclass Security_test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t// Set cookie for security test\n\t\t$_COOKIE['ci_csrf_cookie'] = md5(uniqid(mt_rand(), TRUE));\n\n\t\t// Set config for Security class\n\t\t$this->ci_set_config('csrf_protection', TRUE);\n\t\t$this->ci_set_config('csrf_token_name', 'ci_csrf_token');\n\t\t$this->ci_set_config('csrf_cookie_name', 'ci_csrf_cookie');\n\n\t\t$_SERVER['REQUEST_METHOD'] = 'GET';\n\t\t$this->security = new Mock_Core_Security('UTF-8');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_csrf_verify()\n\t{\n\t\t$_SERVER['REQUEST_METHOD'] = 'GET';\n\n\t\t$this->assertInstanceOf('CI_Security', $this->security->csrf_verify());\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_csrf_verify_invalid()\n\t{\n\t\t// Without issuing $_POST[csrf_token_name], this request will triggering CSRF error\n\t\t$_SERVER['REQUEST_METHOD'] = 'POST';\n\n\t\t$this->setExpectedException('RuntimeException', 'CI Error: The action you have requested is not allowed');\n\n\t\t$this->security->csrf_verify();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_csrf_verify_valid()\n\t{\n\t\t$_SERVER['REQUEST_METHOD'] = 'POST';\n\t\t$_POST[$this->security->csrf_token_name] = $this->security->csrf_hash;\n\n\t\t$this->assertInstanceOf('CI_Security', $this->security->csrf_verify());\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_get_csrf_hash()\n\t{\n\t\t$this->assertEquals($this->security->csrf_hash, $this->security->get_csrf_hash());\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_get_csrf_token_name()\n\t{\n\t\t$this->assertEquals('ci_csrf_token', $this->security->get_csrf_token_name());\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_xss_clean()\n\t{\n\t\t$harm_string = \"Hello, i try to <script>alert('Hack');</script> your site\";\n\n\t\t$harmless_string = $this->security->xss_clean($harm_string);\n\n\t\t$this->assertEquals(\"Hello, i try to [removed]alert&#40;'Hack'&#41;;[removed] your site\", $harmless_string);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_xss_clean_string_array()\n\t{\n\t\t$harm_strings = array(\n\t\t\t\"Hello, i try to <script>alert('Hack');</script> your site\",\n\t\t\t\"Simple clean string\",\n\t\t\t\"Hello, i try to <script>alert('Hack');</script> your site\"\n\t\t);\n\n\t\t$harmless_strings = $this->security->xss_clean($harm_strings);\n\n\t\t$this->assertEquals(\"Hello, i try to [removed]alert&#40;'Hack'&#41;;[removed] your site\", $harmless_strings[0]);\n\t\t$this->assertEquals(\"Simple clean string\", $harmless_strings[1]);\n\t\t$this->assertEquals(\"Hello, i try to [removed]alert&#40;'Hack'&#41;;[removed] your site\", $harmless_strings[2]);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_xss_clean_image_valid()\n\t{\n\t\t$harm_string = '<img src=\"test.png\">';\n\n\t\t$xss_clean_return = $this->security->xss_clean($harm_string, TRUE);\n\n//\t\t$this->assertTrue($xss_clean_return);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_xss_clean_image_invalid()\n\t{\n\t\t$harm_string = '<img src=javascript:alert(String.fromCharCode(88,83,83))>';\n\n\t\t$xss_clean_return = $this->security->xss_clean($harm_string, TRUE);\n\n\t\t$this->assertFalse($xss_clean_return);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_xss_clean_entity_double_encoded()\n\t{\n\t\t$input = '<a href=\"&#38&#35&#49&#48&#54&#38&#35&#57&#55&#38&#35&#49&#49&#56&#38&#35&#57&#55&#38&#35&#49&#49&#53&#38&#35&#57&#57&#38&#35&#49&#49&#52&#38&#35&#49&#48&#53&#38&#35&#49&#49&#50&#38&#35&#49&#49&#54&#38&#35&#53&#56&#38&#35&#57&#57&#38&#35&#49&#49&#49&#38&#35&#49&#49&#48&#38&#35&#49&#48&#50&#38&#35&#49&#48&#53&#38&#35&#49&#49&#52&#38&#35&#49&#48&#57&#38&#35&#52&#48&#38&#35&#52&#57&#38&#35&#52&#49\">Clickhere</a>';\n\t\t$this->assertEquals('<a>Clickhere</a>', $this->security->xss_clean($input));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function text_xss_clean_js_link_removal()\n\t{\n\t\t// This one is to prevent a false positive\n\t\t$this->assertEquals(\n\t\t\t\"<a href=\\\"javascrip\\n<t\\n:alert\\n&#40;1&#41;\\\"\\n>\",\n\t\t\t$this->security->xss_clean(\"<a href=\\\"javascrip\\n<t\\n:alert\\n(1)\\\"\\n>\")\n\t\t);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_xss_clean_js_img_removal()\n\t{\n\t\t$input = '<img src=\"&#38&#35&#49&#48&#54&#38&#35&#57&#55&#38&#35&#49&#49&#56&#38&#35&#57&#55&#38&#35&#49&#49&#53&#38&#35&#57&#57&#38&#35&#49&#49&#52&#38&#35&#49&#48&#53&#38&#35&#49&#49&#50&#38&#35&#49&#49&#54&#38&#35&#53&#56&#38&#35&#57&#57&#38&#35&#49&#49&#49&#38&#35&#49&#49&#48&#38&#35&#49&#48&#50&#38&#35&#49&#48&#53&#38&#35&#49&#49&#52&#38&#35&#49&#48&#57&#38&#35&#52&#48&#38&#35&#52&#57&#38&#35&#52&#49\">Clickhere';\n\t\t$this->assertEquals('<img>', $this->security->xss_clean($input));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_xss_clean_sanitize_naughty_html_tags()\n\t{\n\t\t$this->assertEquals('&lt;unclosedTag', $this->security->xss_clean('<unclosedTag'));\n\t\t$this->assertEquals('&lt;blink&gt;', $this->security->xss_clean('<blink>'));\n\t\t$this->assertEquals('<fubar>', $this->security->xss_clean('<fubar>'));\n\n\t\t$this->assertEquals(\n\t\t\t'<img svg=\"\"> src=\"x\">',\n\t\t\t$this->security->xss_clean('<img <svg=\"\"> src=\"x\">')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\t'<img src=\"b on=\">on=\">\"x onerror=\"alert&#40;1&#41;\">',\n\t\t\t$this->security->xss_clean('<img src=\"b on=\"<x\">on=\">\"x onerror=\"alert(1)\">')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\t\"\\n>&lt;!-\\n<b d=\\\"'e><iframe onload=alert&#40;1&#41; src=x>\\n<a HREF=\\\">\\n\",\n\t\t\t$this->security->xss_clean(\"\\n><!-\\n<b\\n<c d=\\\"'e><iframe onload=alert(1) src=x>\\n<a HREF=\\\"\\\">\\n\")\n\t\t);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_xss_clean_sanitize_naughty_html_attributes()\n\t{\n\t\t$this->assertEquals('<foo xss=removed>', $this->security->xss_clean('<foo onAttribute=\"bar\">'));\n\t\t$this->assertEquals('<foo xss=removed>', $this->security->xss_clean('<foo onAttributeNoQuotes=bar>'));\n\t\t$this->assertEquals('<foo xss=removed>', $this->security->xss_clean('<foo onAttributeWithSpaces = bar>'));\n\t\t$this->assertEquals('<foo prefixOnAttribute=\"bar\">', $this->security->xss_clean('<foo prefixOnAttribute=\"bar\">'));\n\t\t$this->assertEquals('<foo>onOutsideOfTag=test</foo>', $this->security->xss_clean('<foo>onOutsideOfTag=test</foo>'));\n\t\t$this->assertEquals('onNoTagAtAll = true', $this->security->xss_clean('onNoTagAtAll = true'));\n\t\t$this->assertEquals('<foo xss=removed>', $this->security->xss_clean('<foo fscommand=case-insensitive>'));\n\t\t$this->assertEquals('<foo xss=removed>', $this->security->xss_clean('<foo seekSegmentTime=whatever>'));\n\n\t\t$this->assertEquals(\n\t\t\t'<foo bar=\">\" baz=\\'>\\' xss=removed>',\n\t\t\t$this->security->xss_clean('<foo bar=\">\" baz=\\'>\\' onAfterGreaterThan=\"quotes\">')\n\t\t);\n\t\t$this->assertEquals(\n\t\t\t'<foo bar=\">\" baz=\\'>\\' xss=removed>',\n\t\t\t$this->security->xss_clean('<foo bar=\">\" baz=\\'>\\' onAfterGreaterThan=noQuotes>')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\t'<img src=\"x\" on=\"\"> on=&lt;svg&gt; onerror=alert&#40;1&#41;>',\n\t\t\t$this->security->xss_clean('<img src=\"x\" on=\"\"> on=<svg> onerror=alert(1)>')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\t'<img src=\"on=\\'\">\"&lt;svg&gt; onerror=alert&#40;1&#41; onmouseover=alert&#40;1&#41;>',\n\t\t\t$this->security->xss_clean('<img src=\"on=\\'\">\"<svg> onerror=alert(1) onmouseover=alert(1)>')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\t'<img src=\"x\"> on=\\'x\\' onerror=``,alert&#40;1&#41;>',\n\t\t\t$this->security->xss_clean('<img src=\"x\"> on=\\'x\\' onerror=``,alert(1)>')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\t'<a xss=removed>',\n\t\t\t$this->security->xss_clean('<a< onmouseover=\"alert(1)\">')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\t'<img src=\"x\"> on=\\'x\\' onerror=,xssm()>',\n\t\t\t$this->security->xss_clean('<img src=\"x\"> on=\\'x\\' onerror=,xssm()>')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\t'<image src=\"<>\" xss=removed>',\n\t\t\t$this->security->xss_clean('<image src=\"<>\" onerror=\\'alert(1)\\'>')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\t'<b xss=removed>',\n\t\t\t$this->security->xss_clean('<b \"=<= onmouseover=alert(1)>')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\t'<b xss=removed xss=removed>1\">',\n\t\t\t$this->security->xss_clean('<b a=<=\" onmouseover=\"alert(1),1>1\">')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\t'<b x=\" onmouseover=alert&#40;1&#41;//\">',\n\t\t\t$this->security->xss_clean('<b \"=\"< x=\" onmouseover=alert(1)//\">')\n\t\t);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * @depends test_xss_clean_sanitize_naughty_html_tags\n\t * @depends test_xss_clean_sanitize_naughty_html_attributes\n\t */\n\tpublic function test_naughty_html_plus_evil_attributes()\n\t{\n\t\t$this->assertEquals(\n\t\t\t'&lt;svg<img src=\"x\" xss=removed>',\n\t\t\t$this->security->xss_clean('<svg<img > src=\"x\" onerror=\"location=/javascript/.source+/:alert/.source+/(1)/.source\">')\n\t\t);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_xss_hash()\n\t{\n\t\t$this->assertEmpty($this->security->xss_hash);\n\n\t\t// Perform hash\n\t\t$this->security->xss_hash();\n\n\t\t$assertRegExp = method_exists($this, 'assertMatchesRegularExpression')\n\t\t\t? 'assertMatchesRegularExpression'\n\t\t\t: 'assertRegExp';\n\t\t$this->$assertRegExp('#^[0-9a-f]{32}$#iS', $this->security->xss_hash);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_get_random_bytes()\n\t{\n\t\t$length = \"invalid\";\n\t\t$this->assertFalse($this->security->get_random_bytes($length));\n\n\t\t$length = 10;\n\t\t$this->assertNotEmpty($this->security->get_random_bytes($length));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_entity_decode()\n\t{\n\t\t$encoded = '&lt;div&gt;Hello &lt;b&gt;Booya&lt;/b&gt;&lt;/div&gt;';\n\t\t$decoded = $this->security->entity_decode($encoded);\n\n\t\t$this->assertEquals('<div>Hello <b>Booya</b></div>', $decoded);\n\n\t\t$this->assertEquals('colon:',    $this->security->entity_decode('colon&colon;'));\n\t\t$this->assertEquals(\"NewLine\\n\", $this->security->entity_decode('NewLine&NewLine;'));\n\t\t$this->assertEquals(\"Tab\\t\",     $this->security->entity_decode('Tab&Tab;'));\n\t\t$this->assertEquals(\"lpar(\",     $this->security->entity_decode('lpar&lpar;'));\n\t\t$this->assertEquals(\"rpar)\",     $this->security->entity_decode('rpar&rpar;'));\n\n\t\t// Issue #3057 (https://github.com/bcit-ci/CodeIgniter/issues/3057)\n\t\t$this->assertEquals(\n\t\t\t'&foo should not include a semicolon',\n\t\t\t$this->security->entity_decode('&foo should not include a semicolon')\n\t\t);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_sanitize_filename()\n\t{\n\t\t$filename = './<!--foo-->';\n\t\t$safe_filename = $this->security->sanitize_filename($filename);\n\n\t\t$this->assertEquals('foo', $safe_filename);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_strip_image_tags()\n\t{\n\t\t$imgtags = array(\n\t\t\t'<img src=\"smiley.gif\" alt=\"Smiley face\" height=\"42\" width=\"42\">',\n\t\t\t'<img alt=\"Smiley face\" height=\"42\" width=\"42\" src=\"smiley.gif\">',\n\t\t\t'<img src=\"https://www.w3schools.com/images/w3schools_green.jpg\">',\n\t\t\t'<img src=\"/img/sunset.gif\" height=\"100%\" width=\"100%\">',\n\t\t\t'<img src=\"mdn-logo-sm.png\" alt=\"MD Logo\" srcset=\"mdn-logo-HD.png 2x, mdn-logo-small.png 15w, mdn-banner-HD.png 100w 2x\" />',\n\t\t\t'<img sqrc=\"/img/sunset.gif\" height=\"100%\" width=\"100%\">',\n\t\t\t'<img srqc=\"/img/sunset.gif\" height=\"100%\" width=\"100%\">',\n\t\t\t'<img srcq=\"/img/sunset.gif\" height=\"100%\" width=\"100%\">',\n\t\t\t'<img src=non-quoted.attribute foo=\"bar\">'\n\t\t);\n\n\t\t$urls = array(\n\t\t\t'smiley.gif',\n\t\t\t'smiley.gif',\n\t\t\t'https://www.w3schools.com/images/w3schools_green.jpg',\n\t\t\t'/img/sunset.gif',\n\t\t\t'mdn-logo-sm.png',\n\t\t\t'<img sqrc=\"/img/sunset.gif\" height=\"100%\" width=\"100%\">',\n\t\t\t'<img srqc=\"/img/sunset.gif\" height=\"100%\" width=\"100%\">',\n\t\t\t'<img srcq=\"/img/sunset.gif\" height=\"100%\" width=\"100%\">',\n\t\t\t'non-quoted.attribute'\n\t\t);\n\n\t\tfor ($i = 0; $i < count($imgtags); $i++)\n\t\t{\n\t\t\t$this->assertEquals($urls[$i], $this->security->strip_image_tags($imgtags[$i]));\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_csrf_set_hash()\n\t{\n\t\t// Set cookie for security test\n\t\t$_COOKIE['ci_csrf_cookie'] = md5(uniqid(mt_rand(), TRUE));\n\n\t\t// Set config for Security class\n\t\t$this->ci_set_config('csrf_protection', TRUE);\n\t\t$this->ci_set_config('csrf_token_name', 'ci_csrf_token');\n\n\t\t// leave csrf_cookie_name as blank to test _csrf_set_hash function\n\t\t$this->ci_set_config('csrf_cookie_name', '');\n\n\t\t$this->security = new Mock_Core_Security('UTF-8');\n\n\t\t$this->assertNotEmpty($this->security->get_csrf_hash());\n\t}\n}\n"
  },
  {
    "path": "tests/codeigniter/core/URI_test.php",
    "content": "<?php\n\nclass URI_test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t$this->uri = new Mock_Core_URI();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/* As of the following commit, _set_uri_string() is a protected method:\n\n\t\thttps://github.com/bcit-ci/CodeIgniter/commit/d461934184d95b0cfb2feec93f27b621ef72a5c2\n\n\tpublic function test_set_uri_string()\n\t{\n\t\t// Slashes get killed\n\t\t$this->uri->_set_uri_string('/');\n\t\t$this->assertEquals('', $this->uri->uri_string);\n\n\t\t$this->uri->_set_uri_string('nice/uri');\n\t\t$this->assertEquals('nice/uri', $this->uri->uri_string);\n\t}\n\t*/\n\n\t// --------------------------------------------------------------------\n\n\t/*\n\n\t\tThis has been moved to the constructor\n\n\tpublic function test_fetch_uri_string()\n\t{\n\t\tdefine('SELF', 'index.php');\n\n\t\t// uri_protocol: AUTO\n\t\t$this->uri->config->set_item('uri_protocol', 'AUTO');\n\n\t\t// Test a variety of request uris\n\t\t$requests = array(\n\t\t\t'/index.php/controller/method' => 'controller/method',\n\t\t\t'/index.php?/controller/method' => 'controller/method',\n\t\t\t'/index.php?/controller/method/?var=foo' => 'controller/method'\n\t\t);\n\n\t\tforeach ($requests as $request => $expected)\n\t\t{\n\t\t\t$_SERVER['SCRIPT_NAME'] = '/index.php';\n\t\t\t$_SERVER['REQUEST_URI'] = $request;\n\n\t\t\t$this->uri->_fetch_uri_string();\n\t\t\t$this->assertEquals($expected, $this->uri->uri_string);\n\t\t}\n\n\t\t// Test a subfolder\n\t\t$_SERVER['SCRIPT_NAME'] = '/subfolder/index.php';\n\t\t$_SERVER['REQUEST_URI'] = '/subfolder/index.php/controller/method';\n\n\t\t$this->uri->_fetch_uri_string();\n\t\t$this->assertEquals('controller/method', $this->uri->uri_string);\n\n\t\t// death to request uri\n\t\tunset($_SERVER['REQUEST_URI']);\n\n\t\t// life to path info\n\t\t$_SERVER['PATH_INFO'] = '/controller/method/';\n\n\t\t$this->uri->_fetch_uri_string();\n\t\t$this->assertEquals('controller/method', $this->uri->uri_string);\n\n\t\t// death to path info\n\t\t// At this point your server must be seriously drunk\n\t\tunset($_SERVER['PATH_INFO']);\n\n\t\t$_SERVER['QUERY_STRING'] = '/controller/method/';\n\n\t\t$this->uri->_fetch_uri_string();\n\t\t$this->assertEquals('controller/method', $this->uri->uri_string);\n\n\t\t// At this point your server is a labotomy victim\n\t\tunset($_SERVER['QUERY_STRING']);\n\n\t\t$_GET['/controller/method/'] = '';\n\n\t\t$this->uri->_fetch_uri_string();\n\t\t$this->assertEquals('controller/method', $this->uri->uri_string);\n\n\t\t// Test coverage implies that these will work\n\t\t// uri_protocol: REQUEST_URI\n\t\t// uri_protocol: CLI\n\t}\n\t*/\n\n\t// --------------------------------------------------------------------\n\n\t/*\n\n\t\tThis has been moved into _set_uri_string()\n\n\tpublic function test_explode_segments()\n\t{\n\t\t// Let's test the function's ability to clean up this mess\n\t\t$uris = array(\n\t\t\t'test/uri' => array('test', 'uri'),\n\t\t\t'/test2/uri2' => array('test2', 'uri2'),\n\t\t\t'//test3/test3///' => array('test3', 'test3')\n\t\t);\n\n\t\tforeach ($uris as $uri => $a)\n\t\t{\n\t\t\t$this->uri->segments = array();\n\t\t\t$this->uri->uri_string = $uri;\n\t\t\t$this->uri->_explode_segments();\n\n\t\t\t$this->assertEquals($a, $this->uri->segments);\n\t\t}\n\t}\n\t*/\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * @runInSeparateProcess\n\t */\n\tpublic function test_filter_uri_passing()\n\t{\n\t\tdefine('UTF8_ENABLED', FALSE);\n\n\t\t$this->uri->_set_permitted_uri_chars('a-z 0-9~%.:_\\-');\n\n\t\t$str = 'abc01239~%.:_-';\n\t\t$this->uri->filter_uri($str);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * @runInSeparateProcess\n\t */\n\tpublic function test_filter_uri_throws_error()\n\t{\n\t\tdefine('UTF8_ENABLED', FALSE);\n\t\t$this->setExpectedException('RuntimeException');\n\n\t\t$this->uri->config->set_item('enable_query_strings', FALSE);\n\t\t$this->uri->_set_permitted_uri_chars('a-z 0-9~%.:_\\-');\n\t\t$segment = '$this()'; // filter_uri() accepts by reference\n\t\t$this->uri->filter_uri($segment);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_segment()\n\t{\n\t\t$this->uri->segments = array(1 => 'controller');\n\t\t$this->assertEquals($this->uri->segment(1), 'controller');\n\t\t$this->assertEquals($this->uri->segment(2, 'default'), 'default');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_rsegment()\n\t{\n\t\t$this->uri->rsegments = array(1 => 'method');\n\t\t$this->assertEquals($this->uri->rsegment(1), 'method');\n\t\t$this->assertEquals($this->uri->rsegment(2, 'default'), 'default');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_uri_to_assoc()\n\t{\n\t\t$this->uri->segments = array('a', '1', 'b', '2', 'c', '3');\n\n\t\t$this->assertEquals(\n\t\t\tarray('a' => '1', 'b' => '2', 'c' => '3'),\n\t\t\t$this->uri->uri_to_assoc(1)\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\tarray('b' => '2', 'c' => '3'),\n\t\t\t$this->uri->uri_to_assoc(3)\n\t\t);\n\n\t\t$this->uri->keyval = array(); // reset cache\n\t\t$this->uri->segments = array('a', '1', 'b', '2', 'c');\n\n\t\t$this->assertEquals(\n\t\t\tarray('a' => '1', 'b' => '2', 'c' => FALSE),\n\t\t\t$this->uri->uri_to_assoc(1)\n\t\t);\n\n\t\t$this->uri->keyval = array(); // reset cache\n\t\t$this->uri->segments = array('a', '1');\n\n\t\t// test default\n\t\t$this->assertEquals(\n\t\t\tarray('a' => '1', 'b' => FALSE),\n\t\t\t$this->uri->uri_to_assoc(1, array('a', 'b'))\n\t\t);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_ruri_to_assoc()\n\t{\n\t\t$this->uri->rsegments = array('x', '1', 'y', '2', 'z', '3');\n\n\t\t$this->assertEquals(\n\t\t\tarray('x' => '1', 'y' => '2', 'z' => '3'),\n\t\t\t$this->uri->ruri_to_assoc(1)\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\tarray('y' => '2', 'z' => '3'),\n\t\t\t$this->uri->ruri_to_assoc(3)\n\t\t);\n\n\t\t$this->uri->keyval = array(); // reset cache\n\t\t$this->uri->rsegments = array('x', '1', 'y', '2', 'z');\n\n\t\t$this->assertEquals(\n\t\t\tarray('x' => '1', 'y' => '2', 'z' => FALSE),\n\t\t\t$this->uri->ruri_to_assoc(1)\n\t\t);\n\n\t\t$this->uri->keyval = array(); // reset cache\n\t\t$this->uri->rsegments = array('x', '1');\n\n\t\t// test default\n\t\t$this->assertEquals(\n\t\t\tarray('x' => '1', 'y' => FALSE),\n\t\t\t$this->uri->ruri_to_assoc(1, array('x', 'y'))\n\t\t);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_assoc_to_uri()\n\t{\n\t\t$this->uri->config->set_item('uri_string_slashes', 'none');\n\t\t$this->assertEquals('a/1/b/2', $this->uri->assoc_to_uri(array('a' => '1', 'b' => '2')));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_slash_segment()\n\t{\n\t\t$this->uri->segments[1] = 'segment';\n\t\t$this->uri->rsegments[1] = 'segment';\n\n\t\t$this->assertEquals('/segment/', $this->uri->slash_segment(1, 'both'));\n\t\t$this->assertEquals('/segment/', $this->uri->slash_rsegment(1, 'both'));\n\n\t\t$a = '/segment';\n\t\t$this->assertEquals('/segment', $this->uri->slash_segment(1, 'leading'));\n\t\t$this->assertEquals('/segment', $this->uri->slash_rsegment(1, 'leading'));\n\n\t\t$this->assertEquals('segment/', $this->uri->slash_segment(1, 'trailing'));\n\t\t$this->assertEquals('segment/', $this->uri->slash_rsegment(1, 'trailing'));\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/core/Utf8_test.php",
    "content": "<?php\n\n/**\n * @runTestsInSeparateProcesses\n */\nclass Utf8_test extends CI_TestCase {\n\n\tpublic function test___constructUTF8_ENABLED()\n\t{\n\t\tif ( ! defined('PREG_BAD_UTF8_ERROR') OR (ICONV_ENABLED === FALSE && MB_ENABLED === FALSE))\n\t\t{\n\t\t\treturn $this->markTestSkipped('PCRE_UTF8 and/or both ext/mbstring & ext/iconv are unavailable');\n\t\t}\n\n\t\tnew CI_Utf8('UTF-8');\n\t\t$this->assertTrue(UTF8_ENABLED);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test__constructUTF8_DISABLED()\n\t{\n\t\tnew CI_Utf8('WINDOWS-1251');\n\t\t$this->assertFalse(UTF8_ENABLED);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * is_ascii() test\n\t *\n\t * Note: DO NOT move this below test_clean_string()\n\t */\n\tpublic function test_is_ascii()\n\t{\n\t\t$utf8 = new CI_Utf8('UTF-8');\n\t\t$this->assertTrue($utf8->is_ascii('foo bar'));\n\t\t$this->assertFalse($utf8->is_ascii('тест'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * clean_string() test\n\t *\n\t * @depends\ttest_is_ascii\n\t * @covers\tCI_Utf8::clean_string\n\t */\n\tpublic function test_clean_string()\n\t{\n\t\t$utf8 = new CI_Utf8('UTF-8');\n\t\t$this->assertEquals('foo bar', $utf8->clean_string('foo bar'));\n\n\t\t$illegal_utf8 = \"\\xc0тест\";\n\t\tif (MB_ENABLED)\n\t\t{\n\t\t\t$this->assertEquals('тест', $utf8->clean_string($illegal_utf8));\n\t\t}\n\t\telseif (ICONV_ENABLED)\n\t\t{\n\t\t\t// This is a known issue, iconv doesn't always work with //IGNORE\n\t\t\t$this->assertContains($utf8->clean_string($illegal_utf8), array('тест', ''));\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$this->assertEquals($illegal_utf8, $utf8->clean_string($illegal_utf8));\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * convert_to_utf8() test\n\t *\n\t * @covers\tCI_Utf8::convert_to_utf8\n\t */\n\tpublic function test_convert_to_utf8()\n\t{\n\t\t$utf8 = new CI_Utf8('UTF-8');\n\t\tif (MB_ENABLED OR ICONV_ENABLED)\n\t\t{\n\t\t\t$this->assertEquals('тест', $utf8->convert_to_utf8('', 'WINDOWS-1251'));\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$this->assertFalse($utf8->convert_to_utf8('', 'WINDOWS-1251'));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/core/compat/hash_test.php",
    "content": "<?php\n\nclass hash_test extends CI_TestCase {\n\n\tpublic function test_bootstrap()\n\t{\n\t\tif (is_php('5.6'))\n\t\t{\n\t\t\treturn $this->markTestSkipped('ext/hash is available on PHP 5.6');\n\t\t}\n\n\t\t$this->assertTrue(function_exists('hash_equals'));\n\t\tis_php('5.5') OR $this->assertTrue(function_exists('hash_pbkdf2'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * hash_equals() test\n\t *\n\t * Borrowed from PHP's own tests\n\t *\n\t * @depends\ttest_bootstrap\n\t */\n\tpublic function test_hash_equals()\n\t{\n\t\t$this->assertTrue(hash_equals('same', 'same'));\n\t\t$this->assertFalse(hash_equals('not1same', 'not2same'));\n\t\t$this->assertFalse(hash_equals('short', 'longer'));\n\t\t$this->assertFalse(hash_equals('longer', 'short'));\n\t\t$this->assertFalse(hash_equals('', 'notempty'));\n\t\t$this->assertFalse(hash_equals('notempty', ''));\n\t\t$this->assertTrue(hash_equals('', ''));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * hash_pbkdf2() test\n\t *\n\t * Borrowed from PHP's own tests\n\t *\n\t * @depends\ttest_bootstrap\n\t */\n\tpublic function test_hash_pbkdf2()\n\t{\n\t\tif (is_php('5.5'))\n\t\t{\n\t\t\treturn $this->markTestSkipped('hash_pbkdf2() is available on PHP 5.5');\n\t\t}\n\n\t\t$this->assertEquals('0c60c80f961f0e71f3a9', hash_pbkdf2('sha1', 'password', 'salt', 1, 20));\n\t\t$this->assertEquals(\n\t\t\t\"\\x0c\\x60\\xc8\\x0f\\x96\\x1f\\x0e\\x71\\xf3\\xa9\\xb5\\x24\\xaf\\x60\\x12\\x06\\x2f\\xe0\\x37\\xa6\",\n\t\t\thash_pbkdf2('sha1', 'password', 'salt', 1, 20, TRUE)\n\t\t);\n\t\t$this->assertEquals('3d2eec4fe41c849b80c8d8366', hash_pbkdf2('sha1', 'passwordPASSWORDpassword', 'saltSALTsaltSALTsaltSALTsaltSALTsalt', 4096, 25));\n\t\t$this->assertEquals(\n\t\t\t\"\\x3d\\x2e\\xec\\x4f\\xe4\\x1c\\x84\\x9b\\x80\\xc8\\xd8\\x36\\x62\\xc0\\xe4\\x4a\\x8b\\x29\\x1a\\x96\\x4c\\xf2\\xf0\\x70\\x38\",\n\t\t\thash_pbkdf2('sha1', 'passwordPASSWORDpassword', 'saltSALTsaltSALTsaltSALTsaltSALTsalt', 4096, 25, TRUE)\n\t\t);\n\t\t$this->assertEquals('120fb6cffcf8b32c43e7', hash_pbkdf2('sha256', 'password', 'salt', 1, 20));\n\t\t$this->assertEquals(\n\t\t\t\"\\x12\\x0f\\xb6\\xcf\\xfc\\xf8\\xb3\\x2c\\x43\\xe7\\x22\\x52\\x56\\xc4\\xf8\\x37\\xa8\\x65\\x48\\xc9\",\n\t\t\thash_pbkdf2('sha256', 'password', 'salt', 1, 20, TRUE)\n\t\t);\n\t\t$this->assertEquals(\n\t\t\t'348c89dbcbd32b2f32d814b8116e84cf2b17347e',\n\t\t\thash_pbkdf2('sha256', 'passwordPASSWORDpassword', 'saltSALTsaltSALTsaltSALTsaltSALTsalt', 4096, 40)\n\t\t);\n\t\t$this->assertEquals(\n\t\t\t\"\\x34\\x8c\\x89\\xdb\\xcb\\xd3\\x2b\\x2f\\x32\\xd8\\x14\\xb8\\x11\\x6e\\x84\\xcf\\x2b\\x17\\x34\\x7e\\xbc\\x18\\x00\\x18\\x1c\\x4e\\x2a\\x1f\\xb8\\xdd\\x53\\xe1\\xc6\\x35\\x51\\x8c\\x7d\\xac\\x47\\xe9\",\n\t\t\thash_pbkdf2('sha256', 'passwordPASSWORDpassword', 'saltSALTsaltSALTsaltSALTsaltSALTsalt', 4096, 40, TRUE)\n\t\t);\n\t}\n\n}"
  },
  {
    "path": "tests/codeigniter/core/compat/mbstring_test.php",
    "content": "<?php\n\nclass mbstring_test extends CI_TestCase {\n\n\tpublic function test_bootstrap()\n\t{\n\t\tif (MB_ENABLED)\n\t\t{\n\t\t\treturn $this->markTestSkipped('ext/mbstring is loaded');\n\t\t}\n\n\t\t$this->assertTrue(function_exists('mb_strlen'));\n\t\t$this->assertTrue(function_exists('mb_substr'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @depends\ttest_bootstrap\n\t */\n\tpublic function test_mb_strlen()\n\t{\n\t\t$this->assertEquals(ICONV_ENABLED ? 4 : 8, mb_strlen('тест'));\n\t\t$this->assertEquals(ICONV_ENABLED ? 4 : 8, mb_strlen('тест', 'UTF-8'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @depends\ttest_bootstrap\n\t */\n\tpublic function test_mb_strpos()\n\t{\n\t\t$this->assertEquals(ICONV_ENABLED ? 2 : 4, mb_strpos('тест', 'с'));\n\t\t$this->assertFalse(mb_strpos('тест', 'с', 3));\n\t\t$this->assertEquals(ICONV_ENABLED ? 2 : 4, mb_strpos('тест', 'с', 1, 'UTF-8'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @depends\ttest_bootstrap\n\t */\n\tpublic function test_mb_substr()\n\t{\n\t\t$this->assertEquals(ICONV_ENABLED ? 'стинг' : 'естинг', mb_substr('тестинг', 2));\n\t\t$this->assertEquals(ICONV_ENABLED ? 'нг' : 'г', mb_substr('тестинг', -2));\n\t\t$this->assertEquals(ICONV_ENABLED ? 'ст' : 'е', mb_substr('тестинг', 2, 2));\n\t\t$this->assertEquals(ICONV_ENABLED ? 'стинг' : 'естинг', mb_substr('тестинг', 2, NULL, 'UTF-8'));\n\t\t$this->assertEquals(ICONV_ENABLED ? 'нг' : 'г', mb_substr('тестинг', -2, NULL, 'UTF-8'));\n\t\t$this->assertEquals(ICONV_ENABLED ? 'ст' : 'е', mb_substr('тестинг', 2, 2, 'UTF-8'));\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/core/compat/password_test.php",
    "content": "<?php\n\nclass password_test extends CI_TestCase {\n\n\tpublic function test_bootstrap()\n\t{\n\t\tif (is_php('5.5'))\n\t\t{\n\t\t\treturn $this->markTestSkipped('ext/standard/password is available on PHP 5.5');\n\t\t}\n\t\t// defined as of HHVM 2.3.0, which is also when they introduce password_*() as well\n\t\t// Note: Do NOT move this after the CRYPT_BLOWFISH check\n\t\telseif (defined('HHVM_VERSION'))\n\t\t{\n\t\t\t$this->markTestSkipped('HHVM 2.3.0+ already has it');\n\t\t}\n\t\telseif ( ! defined('CRYPT_BLOWFISH') OR CRYPT_BLOWFISH !== 1)\n\t\t{\n\t\t\t$this->assertFalse(defined('PASSWORD_BCRYPT'));\n\t\t\treturn $this->markTestSkipped('CRYPT_BLOWFISH is not available');\n\t\t}\n\n\t\t$this->assertTrue(defined('PASSWORD_BCRYPT'));\n\t\t$this->assertTrue(defined('PASSWORD_DEFAULT'));\n\t\t$this->assertEquals(1, PASSWORD_BCRYPT);\n\t\t$this->assertEquals(PASSWORD_BCRYPT, PASSWORD_DEFAULT);\n\t\t$this->assertTrue(function_exists('password_get_info'));\n\t\t$this->assertTrue(function_exists('password_hash'));\n\t\t$this->assertTrue(function_exists('password_needs_rehash'));\n\t\t$this->assertTrue(function_exists('password_verify'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * password_get_info() test\n\t *\n\t * Borrowed from PHP's own tests\n\t *\n\t * @depends\ttest_bootstrap\n\t */\n\tpublic function test_password_get_info()\n\t{\n\t\t$expected = array(\n\t\t\t'algo' => 1,\n\t\t\t'algoName' => 'bcrypt',\n\t\t\t'options' => array('cost' => 10)\n\t\t);\n\n\t\t// default\n\t\t$this->assertEquals($expected, password_get_info('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y'));\n\n\t\t$expected['options']['cost'] = 11;\n\n\t\t// cost\n\t\t$this->assertEquals($expected, password_get_info('$2y$11$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y'));\n\n\t\t$expected = array(\n\t\t\t'algo' => 0,\n\t\t\t'algoName' => 'unknown',\n\t\t\t'options' => array()\n\t\t);\n\n\t\t// invalid length\n\t\t$this->assertEquals($expected, password_get_info('$2y$11$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100'));\n\n\t\t// non-bcrypt\n\t\t$this->assertEquals($expected, password_get_info('$1$rasmusle$rISCgZzpwk3UhDidwXvin0'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * password_hash() test\n\t *\n\t * Borrowed from PHP's own tests\n\t *\n\t * @depends\ttest_bootstrap\n\t */\n\tpublic function test_password_hash()\n\t{\n\t\t// FALSE is returned if no CSPRNG source is available\n\t\tif ( ! defined('MCRYPT_DEV_URANDOM') && ! function_exists('openssl_random_pseudo_bytes')\n\t\t\t&& (DIRECTORY_SEPARATOR !== '/' OR ! is_readable('/dev/arandom') OR ! is_readable('/dev/urandom'))\n\t\t\t)\n\t\t{\n\t\t\t$this->assertFalse(password_hash('foo', PASSWORD_BCRYPT));\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$this->assertEquals(60, strlen(password_hash('foo', PASSWORD_BCRYPT)));\n\t\t\t$this->assertTrue(($hash = password_hash('foo', PASSWORD_BCRYPT)) === crypt('foo', $hash));\n\t\t}\n\n\t\t$this->assertEquals(\n\t\t\t'$2y$07$usesomesillystringfore2uDLvp1Ii2e./U9C8sBjqp8I90dH6hi',\n\t\t\tpassword_hash('rasmuslerdorf', PASSWORD_BCRYPT, array('cost' => 7, 'salt' => 'usesomesillystringforsalt'))\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\t'$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y',\n\t\t\tpassword_hash('test', PASSWORD_BCRYPT, array('salt' => '123456789012345678901'.chr(0)))\n\t\t);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * password_needs_rehash() test\n\t *\n\t * Borrowed from PHP's own tests\n\t *\n\t * @depends\ttest_password_get_info\n\t */\n\tpublic function test_password_needs_rehash()\n\t{\n\t\t// invalid hash: always rehash\n\t\t$this->assertTrue(password_needs_rehash('', PASSWORD_BCRYPT));\n\n\t\t// valid, because it's an unknown algorithm\n\t\t$this->assertFalse(password_needs_rehash('', 0));\n\n\t\t// valid with same cost\n\t\t$this->assertFalse(password_needs_rehash('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', PASSWORD_BCRYPT, array('cost' => 10)));\n\n\t\t// valid with same cost and additional parameters\n\t\t$this->assertFalse(password_needs_rehash('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', PASSWORD_BCRYPT, array('cost' => 10, 'foo' => 3)));\n\n\t\t// invalid: different (lower) cost\n\t\t$this->assertTrue(password_needs_rehash('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', PASSWORD_BCRYPT, array('cost' => 9)));\n\n\t\t// invalid: different (higher) cost\n\t\t$this->assertTrue(password_needs_rehash('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', PASSWORD_BCRYPT, array('cost' => 11)));\n\n\t\t// valid with default cost\n\t\t$this->assertFalse(password_needs_rehash('$2y$'.str_pad(10, 2, '0', STR_PAD_LEFT).'$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', PASSWORD_BCRYPT));\n\n\t\t// invalid: 'foo' is cast to 0\n\t\t$this->assertTrue(password_needs_rehash('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', PASSWORD_BCRYPT, array('cost' => 'foo')));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * password_verify() test\n\t *\n\t * Borrowed from PHP's own tests\n\t *\n\t * @depends\ttest_bootstrap\n\t */\n\tpublic function test_password_verify()\n\t{\n\t\t$this->assertFalse(password_verify(123, 123));\n\t\t$this->assertFalse(password_verify('foo', '$2a$07$usesomesillystringforsalt$'));\n\t\t$this->assertFalse(password_verify('rasmusler', '$2a$07$usesomesillystringfore2uDLvp1Ii2e./U9C8sBjqp8I90dH6hi'));\n\t\t$this->assertTrue(password_verify('rasmuslerdorf', '$2a$07$usesomesillystringfore2uDLvp1Ii2e./U9C8sBjqp8I90dH6hi'));\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/core/compat/standard_test.php",
    "content": "<?php\n\nclass standard_test extends CI_TestCase {\n\n\tpublic function test_bootstrap()\n\t{\n\t\tif (is_php('5.5'))\n\t\t{\n\t\t\treturn $this->markTestSkipped('All array functions are already available on PHP 5.5');\n\t\t}\n\n\t\t$this->assertTrue(function_exists('array_column'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * array_column() test\n\t *\n\t * Borrowed from PHP's own tests\n\t *\n\t * @depends\ttest_bootstrap\n\t */\n\tpublic function test_array_column()\n\t{\n\t\t// Basic tests\n\n\t\t$input = array(\n\t\t\tarray(\n\t\t\t\t'id' => 1,\n\t\t\t\t'first_name' => 'John',\n\t\t\t\t'last_name' => 'Doe'\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'id' => 2,\n\t\t\t\t'first_name' => 'Sally',\n\t\t\t\t'last_name' => 'Smith'\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'id' => 3,\n\t\t\t\t'first_name' => 'Jane',\n\t\t\t\t'last_name' => 'Jones'\n\t\t\t)\n\t\t);\n\n\t\t// Ensure internal array position doesn't break it\n\t\tnext($input);\n\n\t\t$this->assertEquals(\n\t\t\tarray('John', 'Sally', 'Jane'),\n\t\t\tarray_column($input, 'first_name')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\tarray(1, 2, 3),\n\t\t\tarray_column($input, 'id')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\tarray(\n\t\t\t\t1 => 'Doe',\n\t\t\t\t2 => 'Smith',\n\t\t\t\t3 => 'Jones'\n\t\t\t),\n\t\t\tarray_column($input, 'last_name', 'id')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\tarray(\n\t\t\t\t'John' => 'Doe',\n\t\t\t\t'Sally' => 'Smith',\n\t\t\t\t'Jane' => 'Jones'\n\t\t\t),\n\t\t\tarray_column($input, 'last_name', 'first_name')\n\t\t);\n\n\t\t// Object key search\n\n\t\t$f = new Foo();\n\t\t$b = new Bar();\n\n\t\t$this->assertEquals(\n\t\t\tarray('Doe', 'Smith', 'Jones'),\n\t\t\tarray_column($input, $f)\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\tarray(\n\t\t\t\t'John' => 'Doe',\n\t\t\t\t'Sally' => 'Smith',\n\t\t\t\t'Jane' => 'Jones'\n\t\t\t),\n\t\t\tarray_column($input, $f, $b)\n\t\t);\n\n\t\t// NULL parameters\n\n\t\t$input = array(\n\t\t\t456 => array(\n\t\t\t\t'id' => '3',\n\t\t\t\t'title' => 'Foo',\n\t\t\t\t'date' => '2013-03-25'\n\t\t\t),\n\t\t\t457 => array(\n\t\t\t\t'id' => '5',\n\t\t\t\t'title' => 'Bar',\n\t\t\t\t'date' => '2012-05-20'\n\t\t\t)\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\tarray(\n\t\t\t\t3 => array(\n\t\t\t\t\t'id' => '3',\n\t\t\t\t\t'title' => 'Foo',\n\t\t\t\t\t'date' => '2013-03-25'\n\t\t\t\t),\n\t\t\t\t5 => array(\n\t\t\t\t\t'id' => '5',\n\t\t\t\t\t'title' => 'Bar',\n\t\t\t\t\t'date' => '2012-05-20'\n\t\t\t\t)\n\t\t\t),\n\t\t\tarray_column($input, NULL, 'id')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\tarray(\n\t\t\t\tarray(\n\t\t\t\t\t'id' => '3',\n\t\t\t\t\t'title' => 'Foo',\n\t\t\t\t\t'date' => '2013-03-25'\n\t\t\t\t),\n\t\t\t\tarray(\n\t\t\t\t\t'id' => '5',\n\t\t\t\t\t'title' => 'Bar',\n\t\t\t\t\t'date' => '2012-05-20'\n\t\t\t\t)\n\t\t\t),\n\t\t\tarray_column($input, NULL, 'foo')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\tarray(\n\t\t\t\tarray(\n\t\t\t\t\t'id' => '3',\n\t\t\t\t\t'title' => 'Foo',\n\t\t\t\t\t'date' => '2013-03-25'\n\t\t\t\t),\n\t\t\t\tarray(\n\t\t\t\t\t'id' => '5',\n\t\t\t\t\t'title' => 'Bar',\n\t\t\t\t\t'date' => '2012-05-20'\n\t\t\t\t)\n\t\t\t),\n\t\t\tarray_column($input, NULL)\n\t\t);\n\n\t\t// Data types\n\n\t\t$fh = fopen(__FILE__, 'r', TRUE);\n\t\t$stdClass = new stdClass();\n\t\t$input = array(\n\t\t\tarray(\n\t\t\t\t'id' => 1,\n\t\t\t\t'value' => $stdClass\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'id' => 2,\n\t\t\t\t'value' => 34.2345\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'id' => 3,\n\t\t\t\t'value' => TRUE\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'id' => 4,\n\t\t\t\t'value' => FALSE\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'id' => 5,\n\t\t\t\t'value' => NULL\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'id' => 6,\n\t\t\t\t'value' => 1234\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'id' => 7,\n\t\t\t\t'value' => 'Foo'\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'id' => 8,\n\t\t\t\t'value' => $fh\n\t\t\t)\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\tarray(\n\t\t\t\t$stdClass,\n\t\t\t\t34.2345,\n\t\t\t\tTRUE,\n\t\t\t\tFALSE,\n\t\t\t\tNULL,\n\t\t\t\t1234,\n\t\t\t\t'Foo',\n\t\t\t\t$fh\n\t\t\t),\n\t\t\tarray_column($input, 'value')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\tarray(\n\t\t\t\t1 => $stdClass,\n\t\t\t\t2 => 34.2345,\n\t\t\t\t3 => TRUE,\n\t\t\t\t4 => FALSE,\n\t\t\t\t5 => NULL,\n\t\t\t\t6 => 1234,\n\t\t\t\t7 => 'Foo',\n\t\t\t\t8 => $fh\n\t\t\t),\n\t\t\tarray_column($input, 'value', 'id')\n\t\t);\n\n\t\t// Numeric column keys\n\n\t\t$input = array(\n\t\t\tarray('aaa', '111'),\n\t\t\tarray('bbb', '222'),\n\t\t\tarray('ccc', '333', -1 => 'ddd')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\tarray('111', '222', '333'),\n\t\t\tarray_column($input, 1)\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\tarray(\n\t\t\t\t'aaa' => '111',\n\t\t\t\t'bbb' => '222',\n\t\t\t\t'ccc' => '333'\n\t\t\t),\n\t\t\tarray_column($input, 1, 0)\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\tarray(\n\t\t\t\t'aaa' => '111',\n\t\t\t\t'bbb' => '222',\n\t\t\t\t'ccc' => '333'\n\t\t\t),\n\t\t\tarray_column($input, 1, 0.123)\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\tarray(\n\t\t\t\t0 => '111',\n\t\t\t\t1 => '222',\n\t\t\t\t'ddd' => '333'\n\t\t\t),\n\t\t\tarray_column($input, 1, -1)\n\t\t);\n\n\t\t// Non-existing columns\n\n\t\t$this->assertEquals(array(), array_column($input, 2));\n\t\t$this->assertEquals(array(), array_column($input, 'foo'));\n\t\t$this->assertEquals(\n\t\t\tarray('aaa', 'bbb', 'ccc'),\n\t\t\tarray_column($input, 0, 'foo')\n\t\t);\n\t\t$this->assertEquals(array(), array_column($input, 3.14));\n\n\t\t// One-dimensional array\n\t\t$this->assertEquals(array(), array_column(array('foo', 'bar', 'baz'), 1));\n\n\t\t// Columns not present in all rows\n\n\t\t$input = array(\n\t\t\tarray('a' => 'foo', 'b' => 'bar', 'e' => 'bbb'),\n\t\t\tarray('a' => 'baz', 'c' => 'qux', 'd' => 'aaa'),\n\t\t\tarray('a' => 'eee', 'b' => 'fff', 'e' => 'ggg')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\tarray('qux'),\n\t\t\tarray_column($input, 'c')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\tarray('baz' => 'qux'),\n\t\t\tarray_column($input, 'c', 'a')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\tarray(\n\t\t\t\t0 => 'foo',\n\t\t\t\t'aaa' => 'baz',\n\t\t\t\t1 => 'eee'\n\t\t\t),\n\t\t\tarray_column($input, 'a', 'd')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\tarray(\n\t\t\t\t'bbb' => 'foo',\n\t\t\t\t0 => 'baz',\n\t\t\t\t'ggg' => 'eee'\n\t\t\t),\n\t\t\tarray_column($input, 'a', 'e')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\tarray('bar', 'fff'),\n\t\t\tarray_column($input, 'b')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\tarray(\n\t\t\t\t'foo' => 'bar',\n\t\t\t\t'eee' => 'fff'\n\t\t\t),\n\t\t\tarray_column($input, 'b', 'a')\n\t\t);\n\t}\n}\n\n// ------------------------------------------------------------------------\n\nclass Foo {\n\n\tpublic function __toString()\n\t{\n\t\treturn 'last_name';\n\t}\n}\n\nclass Bar {\n\n\tpublic function __toString()\n\t{\n\t\treturn 'first_name';\n\t}\n}\n"
  },
  {
    "path": "tests/codeigniter/database/DB_driver_test.php",
    "content": "<?php\n\nclass DB_driver_test extends CI_TestCase {\n\n\tpublic function test_initialize()\n\t{\n\t\t$config = Mock_Database_DB::config(DB_DRIVER);\n\t\tsscanf(DB_DRIVER, '%[^/]/', $driver_name);\n\t\t$driver = $this->{$driver_name}($config[DB_DRIVER]);\n\t}\n\n\tprotected function pdo($config)\n\t{\n\t\treturn new Mock_Database_Drivers_PDO($config);\n\t}\n\n\tprotected function mysql($config)\n\t{\n\t\treturn new Mock_Database_Drivers_Mysql($config);\n\t}\n\n\tprotected function mysqli($config)\n\t{\n\t\treturn new Mock_Database_Drivers_Mysqli($config);\n\t}\n\n\tprotected function sqlite($config)\n\t{\n\t\treturn new Mock_Database_Drivers_Sqlite($config);\n\t}\n\n\tprotected function pgsql($config)\n\t{\n\t\treturn new Mock_Database_Drivers_Postgre($config);\n\t}\n\n}"
  },
  {
    "path": "tests/codeigniter/database/DB_test.php",
    "content": "<?php\n\nclass DB_test extends CI_TestCase {\n\n\tpublic function test_db_invalid()\n\t{\n\t\t$connection = new Mock_Database_DB(array(\n\t\t\t'undefined' => array(\n\t\t\t\t'dsn' => '',\n\t\t\t\t'hostname' => 'undefined',\n\t\t\t\t'username' => 'undefined',\n\t\t\t\t'password' => 'undefined',\n\t\t\t\t'database' => 'undefined',\n\t\t\t\t'dbdriver' => 'undefined',\n\t\t\t),\n\t\t));\n\n\t\t$this->setExpectedException('RuntimeException', 'CI Error: Invalid DB driver');\n\n\t\tMock_Database_DB::DB($connection->set_dsn('undefined'), TRUE);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_db_valid()\n\t{\n\t\t$config = Mock_Database_DB::config(DB_DRIVER);\n\t\t$connection = new Mock_Database_DB($config);\n\n\t\t// E_DEPRECATED notices thrown by mysql_connect(), mysql_pconnect()\n\t\t// on PHP 5.5+ cause the tests to fail\n\t\tif (DB_DRIVER === 'mysql' && version_compare(PHP_VERSION, '5.5', '>='))\n\t\t{\n\t\t\terror_reporting(E_ALL & ~E_DEPRECATED);\n\t\t}\n\n\t\t$db = Mock_Database_DB::DB($connection->set_dsn(DB_DRIVER), TRUE);\n\n\t\t$this->assertInstanceOf('CI_DB', $db);\n\t\t$this->assertInstanceOf('CI_DB_Driver', $db);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n/*\n\tThis test is unusable, because whoever wrote it apparently thought that\n\tan E_WARNING should equal an Exception and based the whole test suite\n\taround that bogus assumption.\n\n\tpublic function test_db_failover()\n\t{\n\t\t$config = Mock_Database_DB::config(DB_DRIVER);\n\t\t$connection = new Mock_Database_DB($config);\n\t\t$db = Mock_Database_DB::DB($connection->set_dsn(DB_DRIVER.'_failover'), TRUE);\n\n\t\t$this->assertInstanceOf('CI_DB', $db);\n\t\t$this->assertInstanceOf('CI_DB_Driver', $db);\n\t}\n*/\n\n}\n"
  },
  {
    "path": "tests/codeigniter/database/query_builder/count_test.php",
    "content": "<?php\n\nclass Count_test extends CI_TestCase {\n\n\t/**\n\t * @var object Database/Query Builder holder\n\t */\n\tprotected $db;\n\n\tpublic function set_up()\n\t{\n\t\t$this->db = Mock_Database_Schema_Skeleton::init(DB_DRIVER);\n\n\t\tMock_Database_Schema_Skeleton::create_tables();\n\t\tMock_Database_Schema_Skeleton::create_data();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_count_all()\n\t{\n\t\t$this->assertEquals(4, $this->db->count_all('job'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_count_all_results()\n\t{\n\t\t$this->assertEquals(2, $this->db->like('name', 'ian')->count_all_results('job'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_count_all_results_limit()\n\t{\n\t\t$this->assertEquals(1, $this->db->like('name', 'ian')->limit(1)->count_all_results('job'));\n\t}\n\n}"
  },
  {
    "path": "tests/codeigniter/database/query_builder/delete_test.php",
    "content": "<?php\n\nclass Delete_test extends CI_TestCase {\n\n\t/**\n\t * @var object Database/Query Builder holder\n\t */\n\tprotected $db;\n\n\tpublic function set_up()\n\t{\n\t\t$this->db = Mock_Database_Schema_Skeleton::init(DB_DRIVER);\n\n\t\tMock_Database_Schema_Skeleton::create_tables();\n\t\tMock_Database_Schema_Skeleton::create_data();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_delete()\n\t{\n\t\t// Check initial record\n\t\t$job1 = $this->db->where('id', 1)->get('job')->row();\n\n\t\t$this->assertEquals('Developer', $job1->name);\n\n\t\t// Do the delete\n\t\t$this->db->delete('job', array('id' => 1));\n\n\t\t// Check the record\n\t\t$job1 = $this->db->where('id', 1)->get('job');\n\n\t\t$this->assertEmpty($job1->result_array());\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_delete_several_tables()\n\t{\n\t\t// Check initial record\n\t\t$user4 = $this->db->where('id', 4)->get('user')->row();\n\t\t$job4 = $this->db->where('id', 4)->get('job')->row();\n\n\t\t$this->assertEquals('Musician', $job4->name);\n\t\t$this->assertEquals('Chris Martin', $user4->name);\n\n\t\t// Do the delete\n\t\t$this->db->delete(array('job', 'user'), array('id' => 4));\n\n\t\t// Check the record\n\t\t$job4 = $this->db->where('id', 4)->get('job');\n\t\t$user4 = $this->db->where('id', 4)->get('user');\n\n\t\t$this->assertEmpty($job4->result_array());\n\t\t$this->assertEmpty($user4->result_array());\n\t}\n\n}"
  },
  {
    "path": "tests/codeigniter/database/query_builder/distinct_test.php",
    "content": "<?php\n\nclass Distinct_test extends CI_TestCase {\n\n\t/**\n\t * @var object Database/Query Builder holder\n\t */\n\tprotected $db;\n\n\tpublic function set_up()\n\t{\n\t\t$this->db = Mock_Database_Schema_Skeleton::init(DB_DRIVER);\n\n\t\tMock_Database_Schema_Skeleton::create_tables();\n\t\tMock_Database_Schema_Skeleton::create_data();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_distinct()\n\t{\n\t\t$users = $this->db->select('country')\n\t\t\t\t\t->distinct()\n\t\t\t\t\t->get('user')\n\t\t\t\t\t->result_array();\n\n\t\t$this->assertCount(3, $users);\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/database/query_builder/empty_test.php",
    "content": "<?php\n\nclass Empty_test extends CI_TestCase {\n\n\t/**\n\t * @var object Database/Query Builder holder\n\t */\n\tprotected $db;\n\n\tpublic function set_up()\n\t{\n\t\t$this->db = Mock_Database_Schema_Skeleton::init(DB_DRIVER);\n\n\t\tMock_Database_Schema_Skeleton::create_tables();\n\t\tMock_Database_Schema_Skeleton::create_data();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_empty_table()\n\t{\n\t\t// Check initial record\n\t\t$jobs = $this->db->get('job')->result_array();\n\n\t\t$this->assertCount(4, $jobs);\n\n\t\t// Do the empty\n\t\t$this->db->empty_table('job');\n\n\t\t// Check the record\n\t\t$jobs = $this->db->get('job');\n\n\t\t$this->assertEmpty($jobs->result_array());\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/database/query_builder/escape_test.php",
    "content": "<?php\n\nclass Escape_test extends CI_TestCase {\n\n\t/**\n\t * @var object Database/Query Builder holder\n\t */\n\tprotected $db;\n\n\tpublic function set_up()\n\t{\n\t\t$this->db = Mock_Database_Schema_Skeleton::init(DB_DRIVER);\n\n\t\tMock_Database_Schema_Skeleton::create_tables();\n\t\tMock_Database_Schema_Skeleton::create_data();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_escape_like_percent_sign()\n\t{\n\t\t// Escape the like string\n\t\t$string = $this->db->escape_like_str('\\%foo');\n\n\t\tif (strpos(DB_DRIVER, 'mysql') !== FALSE)\n\t\t{\n\t\t\t$sql = \"SELECT `value` FROM `misc` WHERE `key` LIKE '$string%' ESCAPE '!';\";\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$sql = 'SELECT \"value\" FROM \"misc\" WHERE \"key\" LIKE \\''.$string.'%\\' ESCAPE \\'!\\';';\n\t\t}\n\n\t\t$res = $this->db->query($sql)->result_array();\n\n\t\t// Check the result\n\t\t$this->assertCount(1, $res);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_escape_like_backslash_sign()\n\t{\n\t\t// Escape the like string\n\t\t$string = $this->db->escape_like_str('\\\\');\n\n\t\tif (strpos(DB_DRIVER, 'mysql') !== FALSE)\n\t\t{\n\t\t\t$sql = \"SELECT `value` FROM `misc` WHERE `key` LIKE '$string%' ESCAPE '!';\";\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$sql = 'SELECT \"value\" FROM \"misc\" WHERE \"key\" LIKE \\''.$string.'%\\' ESCAPE \\'!\\';';\n\t\t}\n\n\t\t$res = $this->db->query($sql)->result_array();\n\n\t\t// Check the result\n\t\t$this->assertCount(2, $res);\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/database/query_builder/from_test.php",
    "content": "<?php\n\nclass From_test extends CI_TestCase {\n\n\t/**\n\t * @var object Database/Query Builder holder\n\t */\n\tprotected $db;\n\n\tpublic function set_up()\n\t{\n\t\t$this->db = Mock_Database_Schema_Skeleton::init(DB_DRIVER);\n\n\t\tMock_Database_Schema_Skeleton::create_tables();\n\t\tMock_Database_Schema_Skeleton::create_data();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_from_simple()\n\t{\n\t\t$jobs = $this->db->from('job')\n\t\t\t\t\t->get()\n\t\t\t\t\t->result_array();\n\n\t\t$this->assertCount(4, $jobs);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_from_with_where()\n\t{\n\t\t$job1 = $this->db->from('job')\n\t\t\t\t\t->where('id', 1)\n\t\t\t\t\t->get()\n\t\t\t\t\t->row();\n\n\t\t$this->assertEquals('1', $job1->id);\n\t\t$this->assertEquals('Developer', $job1->name);\n\t\t$this->assertEquals('Awesome job, but sometimes makes you bored', $job1->description);\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/database/query_builder/get_test.php",
    "content": "<?php\n\nclass Get_test extends CI_TestCase {\n\n\t/**\n\t * @var object Database/Query Builder holder\n\t */\n\tprotected $db;\n\n\tpublic function set_up()\n\t{\n\t\t$this->db = Mock_Database_Schema_Skeleton::init(DB_DRIVER);\n\n\t\tMock_Database_Schema_Skeleton::create_tables();\n\t\tMock_Database_Schema_Skeleton::create_data();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_get_simple()\n\t{\n\t\t$jobs = $this->db->get('job')->result_array();\n\n\t\t// Dummy jobs contain 4 rows\n\t\t$this->assertCount(4, $jobs);\n\n\t\t// Check rows item\n\t\t$this->assertEquals('Developer', $jobs[0]['name']);\n\t\t$this->assertEquals('Politician', $jobs[1]['name']);\n\t\t$this->assertEquals('Accountant', $jobs[2]['name']);\n\t\t$this->assertEquals('Musician', $jobs[3]['name']);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_get_where()\n\t{\n\t\t$job1 = $this->db->get_where('job', array('id' => 1))->result_array();\n\n\t\t// Dummy jobs contain 1 rows\n\t\t$this->assertCount(1, $job1);\n\n\t\t// Check rows item\n\t\t$this->assertEquals('Developer', $job1[0]['name']);\n\t}\n\n}"
  },
  {
    "path": "tests/codeigniter/database/query_builder/group_test.php",
    "content": "<?php\n\nclass Group_test extends CI_TestCase {\n\n\t/**\n\t * @var object Database/Query Builder holder\n\t */\n\tprotected $db;\n\n\tpublic function set_up()\n\t{\n\t\t$this->db = Mock_Database_Schema_Skeleton::init(DB_DRIVER);\n\n\t\tMock_Database_Schema_Skeleton::create_tables();\n\t\tMock_Database_Schema_Skeleton::create_data();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_group_by()\n\t{\n\t\t$jobs = $this->db->select('name')\n\t\t\t->from('job')\n\t\t\t->group_by('name')\n\t\t\t->get()\n\t\t\t->result_array();\n\n\t\t$this->assertCount(4, $jobs);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_having_by()\n\t{\n\t\t$jobs = $this->db->select('name')\n\t\t\t->from('job')\n\t\t\t->group_by('name')\n\t\t\t->having('SUM(id) > 2')\n\t\t\t->get()\n\t\t\t->result_array();\n\n\t\t$this->assertCount(2, $jobs);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_having_in()\n\t{\n\t\t$jobs = $this->db->select('name')\n\t\t\t->from('job')\n\t\t\t->group_by('name')\n\t\t\t->having_in('SUM(id)', array(1, 2, 5))\n\t\t\t->get()\n\t\t\t->result_array();\n\n\t\t$this->assertCount(2, $jobs);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_or_having_in()\n\t{\n\t\t$jobs = $this->db->select('name')\n\t\t\t->from('job')\n\t\t\t->group_by('name')\n\t\t\t->or_having_in('SUM(id)', array(1, 5))\n\t\t\t->or_having_in('SUM(id)', array(2, 6))\n\t\t\t->get()\n\t\t\t->result_array();\n\n\t\t$this->assertCount(2, $jobs);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_having_not_in()\n\t{\n\t\t$jobs = $this->db->select('name')\n\t\t\t->from('job')\n\t\t\t->group_by('name')\n\t\t\t->having_not_in('SUM(id)', array(3, 6))\n\t\t\t->get()\n\t\t\t->result_array();\n\n\t\t$this->assertCount(3, $jobs);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_or_having_not_in()\n\t{\n\t\t$jobs = $this->db->select('name')\n\t\t\t->from('job')\n\t\t\t->group_by('name')\n\t\t\t->or_having_not_in('SUM(id)', array(1, 2, 3))\n\t\t\t->or_having_not_in('SUM(id)', array(1, 3, 4))\n\t\t\t->get()\n\t\t\t->result_array();\n\n\t\t$this->assertCount(2, $jobs);\n\t}\n}\n"
  },
  {
    "path": "tests/codeigniter/database/query_builder/insert_test.php",
    "content": "<?php\n\nclass Insert_test extends CI_TestCase {\n\n\t/**\n\t * @var object Database/Query Builder holder\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tprotected $db;\n\n\tpublic function set_up()\n\t{\n\t\t$this->db = Mock_Database_Schema_Skeleton::init(DB_DRIVER);\n\n\t\tMock_Database_Schema_Skeleton::create_tables();\n\n\t\t// Truncate the current datas\n\t\t$this->db->truncate('job');\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_insert()\n\t{\n\t\t$job_data = array('id' => 1, 'name' => 'Grocery Sales', 'description' => 'Discount!');\n\n\t\t// Do normal insert\n\t\t$this->assertTrue($this->db->insert('job', $job_data));\n\n\t\t$job1 = $this->db->get('job')->row();\n\n\t\t// Check the result\n\t\t$this->assertEquals('Grocery Sales', $job1->name);\n\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_insert_batch()\n\t{\n\t\t$job_datas = array(\n\t\t\tarray('id' => 2, 'name' => 'Commedian', 'description' => 'Theres something in your teeth'),\n\t\t\tarray('id' => 3, 'name' => 'Cab Driver', 'description' => 'Iam yellow'),\n\t\t);\n\n\t\t// Do insert batch except for sqlite driver\n\t\tif (strpos(DB_DRIVER, 'sqlite') === FALSE)\n\t\t{\n\t\t\t$this->assertEquals(2, $this->db->insert_batch('job', $job_datas));\n\n\t\t\t$job_2 = $this->db->where('id', 2)->get('job')->row();\n\t\t\t$job_3 = $this->db->where('id', 3)->get('job')->row();\n\n\t\t\t// Check the result\n\t\t\t$this->assertEquals('Commedian', $job_2->name);\n\t\t\t$this->assertEquals('Cab Driver', $job_3->name);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "tests/codeigniter/database/query_builder/join_test.php",
    "content": "<?php\n\nclass Join_test extends CI_TestCase {\n\n\t/**\n\t * @var object Database/Query Builder holder\n\t */\n\tprotected $db;\n\n\tpublic function set_up()\n\t{\n\t\t$this->db = Mock_Database_Schema_Skeleton::init(DB_DRIVER);\n\n\t\tMock_Database_Schema_Skeleton::create_tables();\n\t\tMock_Database_Schema_Skeleton::create_data();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_join_simple()\n\t{\n\t\t$job_user = $this->db->select('job.id as job_id, job.name as job_name, user.id as user_id, user.name as user_name')\n\t\t\t\t\t\t\t->from('job')\n\t\t\t\t\t\t\t->join('user', 'user.id = job.id')\n\t\t\t\t\t\t\t->get()\n\t\t\t\t\t\t\t->result_array();\n\n\t\t// Check the result\n\t\t$this->assertEquals('1', $job_user[0]['job_id']);\n\t\t$this->assertEquals('1', $job_user[0]['user_id']);\n\t\t$this->assertEquals('Derek Jones', $job_user[0]['user_name']);\n\t\t$this->assertEquals('Developer', $job_user[0]['job_name']);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_join_escape_is_null()\n\t{\n\t\t$expected = 'SELECT '.$this->db->escape_identifiers('field')\n\t\t\t\t.\"\\nFROM \".$this->db->escape_identifiers('table1')\n\t\t\t\t.\"\\nJOIN \".$this->db->escape_identifiers('table2').' ON '.$this->db->escape_identifiers('field').' IS NULL';\n\n\t\t$this->assertEquals(\n\t\t\t$expected,\n\t\t\t$this->db->select('field')->from('table1')->join('table2', 'field IS NULL')->get_compiled_select()\n\t\t);\n\n\t\t$expected = 'SELECT '.$this->db->escape_identifiers('field')\n\t\t\t\t.\"\\nFROM \".$this->db->escape_identifiers('table1')\n\t\t\t\t.\"\\nJOIN \".$this->db->escape_identifiers('table2').' ON '.$this->db->escape_identifiers('field').' IS NOT NULL';\n\n\t\t$this->assertEquals(\n\t\t\t$expected,\n\t\t\t$this->db->select('field')->from('table1')->join('table2', 'field IS NOT NULL')->get_compiled_select()\n\t\t);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_join_escape_multiple_conditions()\n\t{\n\t\t// We just need a valid query produced, not one that makes sense\n\t\t$fields = array($this->db->protect_identifiers('table1.field1'), $this->db->protect_identifiers('table2.field2'));\n\n\t\t$expected = 'SELECT '.implode(', ', $fields)\n\t\t\t\t.\"\\nFROM \".$this->db->escape_identifiers('table1')\n\t\t\t\t.\"\\nLEFT JOIN \".$this->db->escape_identifiers('table2').' ON '.implode(' = ', $fields)\n\t\t\t\t.' AND '.$fields[0].\" = 'foo' AND \".$fields[1].' = 0';\n\n\t\t$result = $this->db->select('table1.field1, table2.field2')\n\t\t\t\t->from('table1')\n\t\t\t\t->join('table2', \"table1.field1 = table2.field2 AND table1.field1 = 'foo' AND table2.field2 = 0\", 'LEFT')\n\t\t\t\t->get_compiled_select();\n\n\t\t$this->assertEquals($expected, $result);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_join_escape_multiple_conditions_with_parentheses()\n\t{\n\t\t// We just need a valid query produced, not one that makes sense\n\t\t$fields = array($this->db->protect_identifiers('table1.field1'), $this->db->protect_identifiers('table2.field2'));\n\n\t\t$expected = 'SELECT '.implode(', ', $fields)\n\t\t\t\t.\"\\nFROM \".$this->db->escape_identifiers('table1')\n\t\t\t\t.\"\\nRIGHT JOIN \".$this->db->escape_identifiers('table2').' ON '.implode(' = ', $fields)\n\t\t\t\t.' AND ('.$fields[0].\" = 'foo' OR \".$fields[1].' IS NULL)';\n\n\t\t$result = $this->db->select('table1.field1, table2.field2')\n\t\t\t\t->from('table1')\n\t\t\t\t->join('table2', \"table1.field1 = table2.field2 AND (table1.field1 = 'foo' OR table2.field2 IS NULL)\", 'RIGHT')\n\t\t\t\t->get_compiled_select();\n\n\t\t$this->assertEquals($expected, $result);\n\t}\n\n}"
  },
  {
    "path": "tests/codeigniter/database/query_builder/like_test.php",
    "content": "<?php\n\nclass Like_test extends CI_TestCase {\n\n\t/**\n\t * @var object Database/Query Builder holder\n\t */\n\tprotected $db;\n\n\tpublic function set_up()\n\t{\n\t\t$this->db = Mock_Database_Schema_Skeleton::init(DB_DRIVER);\n\n\t\tMock_Database_Schema_Skeleton::create_tables();\n\t\tMock_Database_Schema_Skeleton::create_data();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_like()\n\t{\n\t\t$job1 = $this->db->like('name', 'veloper')\n\t\t\t\t\t\t\t->get('job')\n\t\t\t\t\t\t\t->row();\n\n\t\t// Check the result\n\t\t$this->assertEquals('1', $job1->id);\n\t\t$this->assertEquals('Developer', $job1->name);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_or_like()\n\t{\n\t\t$jobs = $this->db->like('name', 'ian')\n\t\t\t\t\t\t\t->or_like('name', 'veloper')\n\t\t\t\t\t\t\t->get('job')\n\t\t\t\t\t\t\t->result_array();\n\n\t\t// Check the result\n\t\t$this->assertCount(3, $jobs);\n\t\t$this->assertEquals('Developer', $jobs[0]['name']);\n\t\t$this->assertEquals('Politician', $jobs[1]['name']);\n\t\t$this->assertEquals('Musician', $jobs[2]['name']);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_not_like()\n\t{\n\t\t$jobs = $this->db->not_like('name', 'veloper')\n\t\t\t\t\t\t\t->get('job')\n\t\t\t\t\t\t\t->result_array();\n\n\t\t// Check the result\n\t\t$this->assertCount(3, $jobs);\n\t\t$this->assertEquals('Politician', $jobs[0]['name']);\n\t\t$this->assertEquals('Accountant', $jobs[1]['name']);\n\t\t$this->assertEquals('Musician', $jobs[2]['name']);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_or_not_like()\n\t{\n\t\t$jobs = $this->db->like('name', 'an')\n\t\t\t\t\t\t\t->or_not_like('name', 'veloper')\n\t\t\t\t\t\t\t->get('job')\n\t\t\t\t\t\t\t->result_array();\n\n\t\t// Check the result\n\t\t$this->assertCount(3, $jobs);\n\t\t$this->assertEquals('Politician', $jobs[0]['name']);\n\t\t$this->assertEquals('Accountant', $jobs[1]['name']);\n\t\t$this->assertEquals('Musician', $jobs[2]['name']);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * GitHub issue #273\n\t *\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_like_spaces_and_tabs()\n\t{\n\t\t$spaces = $this->db->like('value', '   ')->get('misc')->result_array();\n\t\t$tabs = $this->db->like('value', \"\\t\")->get('misc')->result_array();\n\n\t\t$this->assertCount(1, $spaces);\n\t\t$this->assertCount(1, $tabs);\n\t}\n\n\t/**\n\t * GitHub issue #5462\n\t *\n\t * @see ./mocks/schema/skeleton.php\n\t *\n\t * @dataProvider like_set_side_provider\n\t */\n\tpublic function test_like_set_side($str, $side, $expected_name)\n\t{\n\t\t$actual = $this->db->like('name', $str, $side)->get('job')->result_array();\n\t\t$this->assertCount(1, $actual);\n\t\t$this->assertEquals($expected_name, $actual[0]['name']);\n\t}\n\n\tpublic function like_set_side_provider()\n\t{\n\t\treturn array(\n\t\t\tarray('Developer', 'none', 'Developer'),\n\t\t\tarray('tician', 'before', 'Politician'),\n\t\t\tarray('Accou', 'after', 'Accountant'),\n\t\t\tarray('usicia', 'both', 'Musician'),\n\t\t);\n\t}\n}\n"
  },
  {
    "path": "tests/codeigniter/database/query_builder/limit_test.php",
    "content": "<?php\n\nclass Limit_test extends CI_TestCase {\n\n\t/**\n\t * @var object Database/Query Builder holder\n\t */\n\tprotected $db;\n\n\tpublic function set_up()\n\t{\n\t\t$this->db = Mock_Database_Schema_Skeleton::init(DB_DRIVER);\n\n\t\tMock_Database_Schema_Skeleton::create_tables();\n\t\tMock_Database_Schema_Skeleton::create_data();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_limit()\n\t{\n\t\t$jobs = $this->db->limit(2)\n\t\t                      ->get('job')\n\t\t                      ->result_array();\n\n\t\t$this->assertCount(2, $jobs);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_limit_and_offset()\n\t{\n\t\t$jobs = $this->db->limit(2, 2)\n\t\t                      ->get('job')\n\t\t                      ->result_array();\n\n\t\t$this->assertCount(2, $jobs);\n\t\t$this->assertEquals('Accountant', $jobs[0]['name']);\n\t\t$this->assertEquals('Musician', $jobs[1]['name']);\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/database/query_builder/order_test.php",
    "content": "<?php\n\nclass Order_test extends CI_TestCase {\n\n\t/**\n\t * @var object Database/Query Builder holder\n\t */\n\tprotected $db;\n\n\tpublic function set_up()\n\t{\n\t\t$this->db = Mock_Database_Schema_Skeleton::init(DB_DRIVER);\n\n\t\tMock_Database_Schema_Skeleton::create_tables();\n\t\tMock_Database_Schema_Skeleton::create_data();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_order_ascending()\n\t{\n\t\t$jobs = $this->db->order_by('name', 'asc')\n\t\t                      ->get('job')\n\t\t                      ->result_array();\n\n\t\t// Check the result\n\t\t$this->assertCount(4, $jobs);\n\t\t$this->assertEquals('Accountant', $jobs[0]['name']);\n\t\t$this->assertEquals('Developer', $jobs[1]['name']);\n\t\t$this->assertEquals('Musician', $jobs[2]['name']);\n\t\t$this->assertEquals('Politician', $jobs[3]['name']);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_order_descending()\n\t{\n\t\t$jobs = $this->db->order_by('name', 'desc')\n\t\t                      ->get('job')\n\t\t                      ->result_array();\n\n\t\t$this->assertCount(4, $jobs);\n\t\t$this->assertEquals('Politician', $jobs[0]['name']);\n\t\t$this->assertEquals('Musician', $jobs[1]['name']);\n\t\t$this->assertEquals('Developer', $jobs[2]['name']);\n\t\t$this->assertEquals('Accountant', $jobs[3]['name']);\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/database/query_builder/select_test.php",
    "content": "<?php\n\nclass Select_test extends CI_TestCase {\n\n\t/**\n\t * @var object Database/Query Builder holder\n\t */\n\tprotected $db;\n\n\tpublic function set_up()\n\t{\n\t\t$this->db = Mock_Database_Schema_Skeleton::init(DB_DRIVER);\n\n\t\tMock_Database_Schema_Skeleton::create_tables();\n\t\tMock_Database_Schema_Skeleton::create_data();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_select_only_one_collumn()\n\t{\n\t\t$jobs_name = $this->db->select('name')\n\t\t                      ->get('job')\n\t\t                      ->result_array();\n\n\t\t// Check rows item\n\t\t$this->assertArrayHasKey('name',$jobs_name[0]);\n\t\t$this->assertArrayNotHasKey('id', $jobs_name[0]);\n\t\t$this->assertArrayNotHasKey('description', $jobs_name[0]);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_select_min()\n\t{\n\t\t$job_min = $this->db->select_min('id')\n\t\t                    ->get('job')\n\t\t                    ->row();\n\n\t\t// Minimum id was 1\n\t\t$this->assertEquals('1', $job_min->id);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_select_max()\n\t{\n\t\t$job_max = $this->db->select_max('id')\n\t\t                    ->get('job')\n\t\t                    ->row();\n\n\t\t// Maximum id was 4\n\t\t$this->assertEquals('4', $job_max->id);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_select_avg()\n\t{\n\t\t$job_avg = $this->db->select_avg('id')\n\t\t                    ->get('job')\n\t\t                    ->row();\n\n\t\t// Average should be 2.5\n\t\t$this->assertEquals(2.5, (float) $job_avg->id);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_select_sum()\n\t{\n\t\t$job_sum = $this->db->select_sum('id')\n\t\t                    ->get('job')\n\t\t                    ->row();\n\n\t\t// Sum of ids should be 10\n\t\t$this->assertEquals('10', $job_sum->id);\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/database/query_builder/truncate_test.php",
    "content": "<?php\n\nclass Truncate_test extends CI_TestCase {\n\n\t/**\n\t * @var object Database/Query Builder holder\n\t */\n\tprotected $db;\n\n\tpublic function set_up()\n\t{\n\t\t$this->db = Mock_Database_Schema_Skeleton::init(DB_DRIVER);\n\n\t\tMock_Database_Schema_Skeleton::create_tables();\n\t\tMock_Database_Schema_Skeleton::create_data();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_truncate()\n\t{\n\t\t// Check initial record\n\t\t$jobs = $this->db->get('job')->result_array();\n\t\t$this->assertCount(4, $jobs);\n\n\t\t// Do the empty\n\t\t$this->db->truncate('job');\n\n\t\t// Check the record\n\t\t$jobs = $this->db->get('job');\n\t\t$this->assertEmpty($jobs->result_array());\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_truncate_with_from()\n\t{\n\t\t// Check initial record\n\t\t$users = $this->db->get('user')->result_array();\n\t\t$this->assertCount(4, $users);\n\n\t\t// Do the empty\n\t\t$this->db->from('user')->truncate();\n\n\t\t// Check the record\n\t\t$users = $this->db->get('user');\n\t\t$this->assertEmpty($users->result_array());\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/database/query_builder/update_test.php",
    "content": "<?php\n\nclass Update_test extends CI_TestCase {\n\n\t/**\n\t * @var object Database/Query Builder holder\n\t */\n\tprotected $db;\n\n\tpublic function set_up()\n\t{\n\t\t$this->db = Mock_Database_Schema_Skeleton::init(DB_DRIVER);\n\n\t\tMock_Database_Schema_Skeleton::create_tables();\n\t\tMock_Database_Schema_Skeleton::create_data();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_update()\n\t{\n\t\t// Check initial record\n\t\t$job1 = $this->db->where('id', 1)->get('job')->row();\n\t\t$this->assertEquals('Developer', $job1->name);\n\n\t\t// Do the update\n\t\t$this->db->where('id', 1)->update('job', array('name' => 'Programmer'));\n\n\t\t// Check updated record\n\t\t$job1 = $this->db->where('id', 1)->get('job')->row();\n\t\t$this->assertEquals('Programmer', $job1->name);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_update_with_set()\n\t{\n\t\t// Check initial record\n\t\t$job1 = $this->db->where('id', 4)->get('job')->row();\n\t\t$this->assertEquals('Musician', $job1->name);\n\n\t\t// Do the update\n\t\t$this->db->set('name', 'Vocalist');\n\t\t$this->db->update('job', NULL, 'id = 4');\n\n\t\t// Check updated record\n\t\t$job1 = $this->db->where('id', 4)->get('job')->row();\n\t\t$this->assertEquals('Vocalist', $job1->name);\n\t}\n\n}"
  },
  {
    "path": "tests/codeigniter/database/query_builder/where_test.php",
    "content": "<?php\n\nclass Where_test extends CI_TestCase {\n\n\t/**\n\t * @var object Database/Query Builder holder\n\t */\n\tprotected $db;\n\n\tpublic function set_up()\n\t{\n\t\t$this->db = Mock_Database_Schema_Skeleton::init(DB_DRIVER);\n\n\t\tMock_Database_Schema_Skeleton::create_tables();\n\t\tMock_Database_Schema_Skeleton::create_data();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_where_simple_key_value()\n\t{\n\t\t$job1 = $this->db->where('id', 1)->get('job')->row();\n\n\t\t$this->assertEquals('1', $job1->id);\n\t\t$this->assertEquals('Developer', $job1->name);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_where_custom_key_value()\n\t{\n\t\t$jobs = $this->db->where('id !=', 1)->get('job')->result_array();\n\t\t$this->assertCount(3, $jobs);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_where_associative_array()\n\t{\n\t\t$where = array('id >' => 2, 'name !=' => 'Accountant');\n\t\t$jobs = $this->db->where($where)->get('job')->result_array();\n\n\t\t$this->assertCount(1, $jobs);\n\n\t\t// Should be Musician\n\t\t$job = current($jobs);\n\t\t$this->assertEquals('Musician', $job['name']);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_where_custom_string()\n\t{\n\t\t$where = \"id > 2 AND name != 'Accountant'\";\n\t\t$jobs = $this->db->where($where)->get('job')->result_array();\n\n\t\t$this->assertCount(1, $jobs);\n\n\t\t// Should be Musician\n\t\t$job = current($jobs);\n\t\t$this->assertEquals('Musician', $job['name']);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_where_or()\n\t{\n\t\t$jobs = $this->db->where('name !=', 'Accountant')\n\t\t\t\t\t\t\t->or_where('id >', 3)\n\t\t\t\t\t\t\t->get('job')\n\t\t\t\t\t\t\t->result_array();\n\n\t\t$this->assertCount(3, $jobs);\n\t\t$this->assertEquals('Developer', $jobs[0]['name']);\n\t\t$this->assertEquals('Politician', $jobs[1]['name']);\n\t\t$this->assertEquals('Musician', $jobs[2]['name']);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_where_in()\n\t{\n\t\t$jobs = $this->db->where_in('name', array('Politician', 'Accountant'))\n\t\t\t\t\t\t\t->get('job')\n\t\t\t\t\t\t\t->result_array();\n\n\t\t$this->assertCount(2, $jobs);\n\t\t$this->assertEquals('Politician', $jobs[0]['name']);\n\t\t$this->assertEquals('Accountant', $jobs[1]['name']);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @see ./mocks/schema/skeleton.php\n\t */\n\tpublic function test_where_not_in()\n\t{\n\t\t$jobs = $this->db->where_not_in('name', array('Politician', 'Accountant'))\n\t\t\t\t\t\t\t->get('job')\n\t\t\t\t\t\t\t->result_array();\n\n\t\t$this->assertCount(2, $jobs);\n\t\t$this->assertEquals('Developer', $jobs[0]['name']);\n\t\t$this->assertEquals('Musician', $jobs[1]['name']);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_issue4093()\n\t{\n\t\t$input = 'bar and baz or qux';\n\t\t$sql = $this->db->where('foo', $input)->get_compiled_select('dummy');\n\t\t$this->assertEquals(\"'\".$input.\"'\", substr($sql, -20));\n\t}\n}\n"
  },
  {
    "path": "tests/codeigniter/helpers/array_helper_test.php",
    "content": "<?php\n\nclass Array_helper_test extends CI_TestCase {\n\n\tpublic $my_array = array(\n\t\t'foo'    => 'bar',\n\t\t'sally'  => 'jim',\n\t\t'maggie' => 'bessie',\n\t\t'herb'   => 'cook'\n\t);\n\n\tpublic function set_up()\n\t{\n\t\t$this->helper('array');\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_element_with_existing_item()\n\t{\n\t\t$this->assertEquals(FALSE, element('testing', $this->my_array));\n\t\t$this->assertEquals('not set', element('testing', $this->my_array, 'not set'));\n\t\t$this->assertEquals('bar', element('foo', $this->my_array));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_random_element()\n\t{\n\t\t// Send a string, not an array to random_element\n\t\t$this->assertEquals('my string', random_element('my string'));\n\n\t\t// Test sending an array\n\t\t$this->assertContains(random_element($this->my_array), $this->my_array);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_elements()\n\t{\n\t\t$this->assertEquals('array', gettype(elements('test', $this->my_array)));\n\t\t$this->assertEquals('array', gettype(elements('foo', $this->my_array)));\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/helpers/captcha_helper_test.php",
    "content": "<?php\n\nclass Captcha_helper_test extends CI_TestCase {\n\n\tpublic function test_create_captcha()\n\t{\n\t\t$this->markTestSkipped(\"Can't test\");\n\t}\n\n}"
  },
  {
    "path": "tests/codeigniter/helpers/cookie_helper_test.php",
    "content": "<?php\n\nclass Cookie_helper_test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t$this->helper('cookie');\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tfunction test_set_cookie()\n\t{\n\t\t/*$input_cls = $this->ci_core_class('input');\n\t\t$this->ci_instance_var('input', new $input_cls);\n\n\t\t$this->assertTrue(set_cookie(\n\t\t\t'my_cookie',\n\t\t\t'foobar'\n\t\t));*/\n\n\t\t$this->markTestSkipped('Need to find a way to overcome a headers already set exception');\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tfunction test_get_cookie()\n\t{\n\t\t$_COOKIE['foo'] = 'bar';\n\n\t\t$security = new Mock_Core_Security('UTF-8');\n\t\t$input_cls = $this->ci_core_class('input');\n\t\t$this->ci_instance_var('input', new CI_Input($security));\n\n\t\t$this->assertEquals('bar', get_cookie('foo', FALSE));\n\t\t$this->assertEquals('bar', get_cookie('foo', TRUE));\n\n\t\t$_COOKIE['bar'] = \"Hello, i try to <script>alert('Hack');</script> your site\";\n\n\t\t$this->assertEquals(\"Hello, i try to [removed]alert&#40;'Hack'&#41;;[removed] your site\", get_cookie('bar', TRUE));\n\t\t$this->assertEquals(\"Hello, i try to <script>alert('Hack');</script> your site\", get_cookie('bar', FALSE));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tfunction test_delete_cookie()\n\t{\n\t\t/*$input_cls = $this->ci_core_class('input');\n\t\t$this->ci_instance_var('input', new $input_cls);\n\n\t\t$this->assertTrue(delete_cookie(\n\t\t\t'my_cookie'\n\t\t));*/\n\n\t\t$this->markTestSkipped('Need to find a way to overcome a headers already set exception');\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/helpers/date_helper_test.php",
    "content": "<?php\n\nclass Date_helper_test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t$this->helper('date');\n\t\t$this->time = time();\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_now_local()\n\t{\n\t\t/*\n\n\t\t// This stub job, is simply to cater $config['time_reference']\n\t\t$config = $this->getMockBuilder('CI_Config')->getMock();\n\t\t$config->expects($this->any())\n\t\t\t   ->method('item')\n\t\t\t   ->will($this->returnValue('local'));\n\n\t\t// Add the stub to our test instance\n\t\t$this->ci_instance_var('config', $config);\n\n\t\t*/\n\n\t\t$this->ci_set_config('time_reference', 'local');\n\n\t\t$this->assertEquals(time(), now());\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_now_utc()\n\t{\n\t\t/*\n\n\t\t// This stub job, is simply to cater $config['time_reference']\n\t\t$config = $this->getMockBuilder('CI_Config')->getMock();\n\t\t$config->expects($this->any())\n\t\t\t   ->method('item')\n\t\t\t   ->will($this->returnValue('UTC'));\n\n\t\t// Add the stub to our stdClass\n\t\t$this->ci_instance_var('config', $config);\n\n\t\t*/\n\n\t\t$this->assertEquals(\n\t\t\tmktime(gmdate('G'), gmdate('i'), gmdate('s'), gmdate('n'), gmdate('j'), gmdate('Y')),\n\t\t\tnow('UTC')\n\t\t);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_mdate()\n\t{\n\t\t$this->assertEquals(\n\t\t\tdate('Y-m-d - h:i a', $this->time),\n\t\t\tmdate('%Y-%m-%d - %h:%i %a', $this->time)\n\t\t);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_timespan()\n\t{\n\t\t$this->ci_vfs_clone('system/language/english/date_lang.php');\n\n\t\t$loader_cls = $this->ci_core_class('load');\n\t\t$this->ci_instance_var('load', new $loader_cls);\n\n\t\t$lang_cls = $this->ci_core_class('lang');\n\t\t$this->ci_instance_var('lang', new $lang_cls);\n\n\t\t$this->assertEquals('1 Second', timespan(time(), time()+1));\n\t\t$this->assertEquals('1 Minute', timespan(time(), time()+60));\n\t\t$this->assertEquals('1 Hour', timespan(time(), time()+3600));\n\t\t$this->assertEquals('2 Hours', timespan(time(), time()+7200));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_days_in_month()\n\t{\n\t\t$this->assertEquals(30, days_in_month(06, 2005));\n\t\t$this->assertEquals(28, days_in_month(02, 2011));\n\t\t$this->assertEquals(29, days_in_month(02, 2012));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_local_to_gmt()\n\t{\n\t\t$this->assertEquals(\n\t\t\tmktime(\n\t\t\t\tgmdate('G', $this->time), gmdate('i', $this->time), gmdate('s', $this->time),\n\t\t\t\tgmdate('n', $this->time), gmdate('j', $this->time), gmdate('Y', $this->time)\n\t\t\t),\n\t\t\tlocal_to_gmt($this->time)\n\t\t);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_gmt_to_local()\n\t{\n\t\t$this->assertEquals(1140128493, gmt_to_local('1140153693', 'UM8', TRUE));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_mysql_to_unix()\n\t{\n\t\t$this->assertEquals($this->time, mysql_to_unix(date('Y-m-d H:i:s', $this->time)));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_unix_to_human()\n\t{\n\t\t$this->assertEquals(date('Y-m-d h:i A', $this->time), unix_to_human($this->time));\n\t\t$this->assertEquals(date('Y-m-d h:i:s A', $this->time), unix_to_human($this->time, TRUE, 'us'));\n\t\t$this->assertEquals(date('Y-m-d H:i:s', $this->time), unix_to_human($this->time, TRUE, 'eu'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_human_to_unix()\n\t{\n\t\t$date = '2000-12-31 10:00:00 PM';\n\t\t$this->assertEquals(strtotime($date), human_to_unix($date));\n\t\t$this->assertFalse(human_to_unix());\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_timezones()\n\t{\n\t\t$zones = array(\n\t\t\t'UM12'\t\t=> -12,\n\t\t\t'UM11'\t\t=> -11,\n\t\t\t'UM10'\t\t=> -10,\n\t\t\t'UM95'\t\t=> -9.5,\n\t\t\t'UM9'\t\t=> -9,\n\t\t\t'UM8'\t\t=> -8,\n\t\t\t'UM7'\t\t=> -7,\n\t\t\t'UM6'\t\t=> -6,\n\t\t\t'UM5'\t\t=> -5,\n\t\t\t'UM45'\t\t=> -4.5,\n\t\t\t'UM4'\t\t=> -4,\n\t\t\t'UM35'\t\t=> -3.5,\n\t\t\t'UM3'\t\t=> -3,\n\t\t\t'UM2'\t\t=> -2,\n\t\t\t'UM1'\t\t=> -1,\n\t\t\t'UTC'\t\t=> 0,\n\t\t\t'UP1'\t\t=> +1,\n\t\t\t'UP2'\t\t=> +2,\n\t\t\t'UP3'\t\t=> +3,\n\t\t\t'UP35'\t\t=> +3.5,\n\t\t\t'UP4'\t\t=> +4,\n\t\t\t'UP45'\t\t=> +4.5,\n\t\t\t'UP5'\t\t=> +5,\n\t\t\t'UP55'\t\t=> +5.5,\n\t\t\t'UP575'\t\t=> +5.75,\n\t\t\t'UP6'\t\t=> +6,\n\t\t\t'UP65'\t\t=> +6.5,\n\t\t\t'UP7'\t\t=> +7,\n\t\t\t'UP8'\t\t=> +8,\n\t\t\t'UP875'\t\t=> +8.75,\n\t\t\t'UP9'\t\t=> +9,\n\t\t\t'UP95'\t\t=> +9.5,\n\t\t\t'UP10'\t\t=> +10,\n\t\t\t'UP105'\t\t=> +10.5,\n\t\t\t'UP11'\t\t=> +11,\n\t\t\t'UP115'\t\t=> +11.5,\n\t\t\t'UP12'\t\t=> +12,\n\t\t\t'UP1275'\t=> +12.75,\n\t\t\t'UP13'\t\t=> +13,\n\t\t\t'UP14'\t\t=> +14\n\t\t);\n\n\t\tforeach ($zones AS $test => $expected)\n\t\t{\n\t\t\t$this->assertEquals($expected, timezones($test));\n\t\t}\n\n\t\t$this->assertArrayHasKey('UP3', timezones());\n\t\t$this->assertEquals(0, timezones('non_existent'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_date_range()\n\t{\n\t\t$dates = array(\n\t\t\t'29-01-2012', '30-01-2012', '31-01-2012',\n\t\t\t'01-02-2012', '02-02-2012', '03-02-2012',\n\t\t\t'04-02-2012', '05-02-2012', '06-02-2012',\n\t\t\t'07-02-2012', '08-02-2012', '09-02-2012',\n\t\t\t'10-02-2012', '11-02-2012', '12-02-2012',\n\t\t\t'13-02-2012', '14-02-2012', '15-02-2012',\n\t\t\t'16-02-2012', '17-02-2012', '18-02-2012',\n\t\t\t'19-02-2012', '20-02-2012', '21-02-2012',\n\t\t\t'22-02-2012', '23-02-2012', '24-02-2012',\n\t\t\t'25-02-2012', '26-02-2012', '27-02-2012',\n\t\t\t'28-02-2012', '29-02-2012', '01-03-2012'\n\t\t);\n\n\t\t$this->assertEquals($dates, date_range(mktime(12, 0, 0, 1, 29, 2012), mktime(12, 0, 0, 3, 1, 2012), TRUE, 'd-m-Y'));\n\t\tarray_pop($dates);\n\t\t$this->assertEquals($dates, date_range(mktime(12, 0, 0, 1, 29, 2012), 31, FALSE, 'd-m-Y'));\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/helpers/directory_helper_test.php",
    "content": "<?php\n\nclass Directory_helper_test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t$this->helper('directory');\n\n\t\tvfsStreamWrapper::register();\n\t\tvfsStreamWrapper::setRoot(new vfsStreamDirectory('testDir'));\n\n\t\t$this->_test_dir = vfsStreamWrapper::getRoot();\n\t}\n\n\tpublic function test_directory_map()\n\t{\n\t\t$ds = DIRECTORY_SEPARATOR;\n\n\t\t$structure = array(\n\t\t\t'libraries' => array(\n\t\t\t\t'benchmark.html' => '',\n\t\t\t\t'database' => array('active_record.html' => '', 'binds.html' => ''),\n\t\t\t\t'email.html' => '',\n\t\t\t\t'0' => '',\n\t\t\t\t'.hiddenfile.txt' => ''\n\t\t\t)\n\t\t);\n\n\t\tvfsStream::create($structure, $this->_test_dir);\n\n\t\t// is_dir(), opendir(), etc. seem to fail on Windows + vfsStream when there are trailing backslashes in directory names\n\t\tif ( ! is_dir(vfsStream::url('testDir').DIRECTORY_SEPARATOR))\n\t\t{\n\t\t\t$this->markTestSkipped(\"Can't test this under Windows\");\n\t\t\treturn;\n\t\t}\n\n\t\t// test default recursive behavior\n\t\t$expected = array(\n\t\t\t'libraries'.$ds => array(\n\t\t\t\t'benchmark.html',\n\t\t\t\t'database'.$ds => array('active_record.html', 'binds.html'),\n\t\t\t\t'email.html',\n\t\t\t\t'0'\n\t\t\t)\n\t\t);\n\n\t\t$this->assertEquals($expected, directory_map(vfsStream::url('testDir')));\n\n\t\t// test detection of hidden files\n\t\t$expected['libraries'.$ds][] = '.hiddenfile.txt';\n\n\t\t$this->assertEquals($expected, directory_map(vfsStream::url('testDir'), 0, TRUE));\n\n\t\t// test recursion depth behavior\n\t\t$this->assertEquals(array('libraries'.$ds), directory_map(vfsStream::url('testDir'), 1));\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/helpers/download_helper_test.php",
    "content": "<?php\n\nclass Download_helper_test extends CI_TestCase {\n\n\tpublic function test_force_download()\n\t{\n\t\t$this->markTestSkipped('Cant easily test');\n\t}\n\n}"
  },
  {
    "path": "tests/codeigniter/helpers/file_helper_test.php",
    "content": "<?php\n\nclass File_helper_Test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t$this->helper('file');\n\n\t\t$this->_test_dir = vfsStream::setup('');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_octal_permissions()\n\t{\n\t\t$content = 'Jack and Jill went up the mountain to fight a billy goat.';\n\n\t\t$file = vfsStream::newFile('my_file.txt', 0777)\n\t\t\t->withContent($content)\n\t\t\t->lastModified(time() - 86400)\n\t\t\t->at($this->_test_dir);\n\n\t\t$this->assertEquals('777', octal_permissions($file->getPermissions()));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * More tests should happen here, since I'm not hitting the whole function.\n\t */\n\tpublic function test_symbolic_permissions()\n\t{\n\t\t$content = 'Jack and Jill went up the mountain to fight a billy goat.';\n\n\t\t$file = vfsStream::newFile('my_file.txt', 0777)\n\t\t\t->withContent($content)\n\t\t\t->lastModified(time() - 86400)\n\t\t\t->at($this->_test_dir);\n\n\t\t$this->assertEquals('urwxrwxrwx', symbolic_permissions($file->getPermissions()));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_get_mime_by_extension()\n\t{\n\t\t$content = 'Jack and Jill went up the mountain to fight a billy goat.';\n\n\t\t$file = vfsStream::newFile('my_file.txt', 0777)\n\t\t\t->withContent($content)\n\t\t\t->lastModified(time() - 86400)\n\t\t\t->at($this->_test_dir);\n\n\t\t$this->assertEquals('text/plain', get_mime_by_extension(vfsStream::url('my_file.txt')));\n\n\t\t// Test a mime with an array, such as png\n\t\t$file = vfsStream::newFile('foo.png')->at($this->_test_dir);\n\n\t\t$this->assertEquals('image/png', get_mime_by_extension(vfsStream::url('foo.png')));\n\n\t\t// Test a file not in the mimes array\n\t\t$file = vfsStream::newFile('foo.blarfengar')->at($this->_test_dir);\n\n\t\t$this->assertFalse(get_mime_by_extension(vfsStream::url('foo.blarfengar')));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_get_file_info()\n\t{\n\t\t// Test Bad File\n\t\t$this->assertFalse(get_file_info('i_am_bad_boo'));\n\n\t\t// Test the rest\n\n\t\t// First pass in an array\n\t\t$vals = array(\n\t\t\t'name', 'server_path', 'size', 'date',\n\t\t\t'readable', 'writable', 'executable', 'fileperms'\n\t\t);\n\n\t\t$this->_test_get_file_info($vals);\n\n\t\t// Test passing in vals as a string.\n\t\t$this->_test_get_file_info(implode(', ', $vals));\n\t}\n\n\tprivate function _test_get_file_info($vals)\n\t{\n\t\t$content = 'Jack and Jill went up the mountain to fight a billy goat.';\n\t\t$last_modified = time() - 86400;\n\n\t\t$file = vfsStream::newFile('my_file.txt', 0777)\n\t\t\t->withContent($content)\n\t\t\t->lastModified($last_modified)\n\t\t\t->at($this->_test_dir);\n\n\t\t$ret_values = array(\n\t\t\t'name'        => 'my_file.txt',\n\t\t\t'server_path' => 'vfs://my_file.txt',\n\t\t\t'size'        => 57,\n\t\t\t'date'        => $last_modified,\n\t\t\t'readable'    => TRUE,\n\t\t\t'writable'    => TRUE,\n\t\t\t'executable'  => TRUE,\n\t\t\t'fileperms'   => 33279\n\t\t);\n\n\t\t$info = get_file_info(vfsStream::url('my_file.txt'), $vals);\n\n\t\tforeach ($info as $k => $v)\n\t\t{\n\t\t\t$this->assertEquals($ret_values[$k], $v);\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_write_file()\n\t{\n\t\t$content = 'Jack and Jill went up the mountain to fight a billy goat.';\n\n\t\t$file = vfsStream::newFile('write.txt', 0777)\n\t\t\t->withContent('')\n\t\t\t->lastModified(time() - 86400)\n\t\t\t->at($this->_test_dir);\n\n\t\t$this->assertTrue(write_file(vfsStream::url('write.txt'), $content));\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/helpers/form_helper_test.php",
    "content": "<?php\n\nclass Form_helper_test extends CI_TestCase\n{\n\tpublic function set_up()\n\t{\n\t\t$this->helper('form');\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_form_hidden()\n\t{\n\t\t$expected = <<<EOH\n\n<input type=\"hidden\" name=\"username\" value=\"johndoe\" />\n\nEOH;\n\n\t\t$this->assertEquals($expected, form_hidden('username', 'johndoe'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_form_input()\n\t{\n\t\t$expected = <<<EOH\n<input type=\"text\" name=\"username\" value=\"johndoe\" id=\"username\" maxlength=\"100\" size=\"50\" style=\"width:50%\"  />\n\nEOH;\n\n\t\t$data = array(\n\t\t\t'name'        => 'username',\n\t\t\t'id'          => 'username',\n\t\t\t'value'       => 'johndoe',\n\t\t\t'maxlength'   => '100',\n\t\t\t'size'        => '50',\n\t\t\t'style'       => 'width:50%',\n\t\t);\n\n\t\t$this->assertEquals($expected, form_input($data));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_form_password()\n\t{\n\t\t$expected = <<<EOH\n<input type=\"password\" name=\"password\" value=\"\"  />\n\nEOH;\n\n\t\t$this->assertEquals($expected, form_password('password'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_form_upload()\n\t{\n\t\t$expected = <<<EOH\n<input type=\"file\" name=\"attachment\"  />\n\nEOH;\n\n\t\t$this->assertEquals($expected, form_upload('attachment'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_form_textarea()\n\t{\n\t\t$expected = <<<EOH\n<textarea name=\"notes\" cols=\"40\" rows=\"10\" >Notes</textarea>\n\nEOH;\n\n\t\t$this->assertEquals($expected, form_textarea('notes', 'Notes'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_form_dropdown()\n\t{\n\t\t$expected = <<<EOH\n<select name=\"shirts\">\n<option value=\"small\">Small Shirt</option>\n<option value=\"med\">Medium Shirt</option>\n<option value=\"large\" selected=\"selected\">Large Shirt</option>\n<option value=\"xlarge\">Extra Large Shirt</option>\n</select>\n\nEOH;\n\n\t\t$options = array(\n\t\t\t'small'\t\t=> 'Small Shirt',\n\t\t\t'med'\t\t=> 'Medium Shirt',\n\t\t\t'large'\t\t=> 'Large Shirt',\n\t\t\t'xlarge'\t=> 'Extra Large Shirt',\n\t\t);\n\n\t\t$this->assertEquals($expected, form_dropdown('shirts', $options, 'large'));\n\n\t\t$expected = <<<EOH\n<select name=\"shirts\" multiple=\"multiple\">\n<option value=\"small\" selected=\"selected\">Small Shirt</option>\n<option value=\"med\">Medium Shirt</option>\n<option value=\"large\" selected=\"selected\">Large Shirt</option>\n<option value=\"xlarge\">Extra Large Shirt</option>\n</select>\n\nEOH;\n\n\t\t$shirts_on_sale = array('small', 'large');\n\n\t\t$this->assertEquals($expected, form_dropdown('shirts', $options, $shirts_on_sale));\n\n\t\t$options = array(\n\t\t\t'Swedish Cars' => array(\n\t\t\t\t'volvo'\t=> 'Volvo',\n\t\t\t\t'saab'\t=> 'Saab'\n\t\t\t),\n\t\t\t'German Cars' => array(\n\t\t\t\t'mercedes'\t=> 'Mercedes',\n\t\t\t\t'audi'\t\t=> 'Audi'\n\t\t\t)\n\t\t);\n\n\t\t$expected = <<<EOH\n<select name=\"cars\" multiple=\"multiple\">\n<optgroup label=\"Swedish Cars\">\n<option value=\"volvo\" selected=\"selected\">Volvo</option>\n<option value=\"saab\">Saab</option>\n</optgroup>\n<optgroup label=\"German Cars\">\n<option value=\"mercedes\">Mercedes</option>\n<option value=\"audi\" selected=\"selected\">Audi</option>\n</optgroup>\n</select>\n\nEOH;\n\n\t\t$this->assertEquals($expected, form_dropdown('cars', $options, array('volvo', 'audi')));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_form_multiselect()\n\t{\n\t\t$expected = <<<EOH\n<select name=\"shirts[]\"  multiple=\"multiple\">\n<option value=\"small\">Small Shirt</option>\n<option value=\"med\" selected=\"selected\">Medium Shirt</option>\n<option value=\"large\" selected=\"selected\">Large Shirt</option>\n<option value=\"xlarge\">Extra Large Shirt</option>\n</select>\n\nEOH;\n\n\t\t$options = array(\n\t\t\t'small'\t\t=> 'Small Shirt',\n\t\t\t'med'\t\t=> 'Medium Shirt',\n\t\t\t'large'\t\t=> 'Large Shirt',\n\t\t\t'xlarge'\t=> 'Extra Large Shirt',\n\t\t);\n\n\t\t$this->assertEquals($expected, form_multiselect('shirts[]', $options, array('med', 'large')));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_form_fieldset()\n\t{\n\t\t$expected = <<<EOH\n<fieldset>\n<legend>Address Information</legend>\n\nEOH;\n\n\t\t$this->assertEquals($expected, form_fieldset('Address Information'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_form_fieldset_close()\n\t{\n\t\t$expected = <<<EOH\n</fieldset></div></div>\nEOH;\n\n\t\t$this->assertEquals($expected, form_fieldset_close('</div></div>'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_form_checkbox()\n\t{\n\t\t$expected = <<<EOH\n<input type=\"checkbox\" name=\"newsletter\" value=\"accept\" checked=\"checked\"  />\n\nEOH;\n\n\t\t$this->assertEquals($expected, form_checkbox('newsletter', 'accept', TRUE));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_form_radio()\n\t{\n\t\t$expected = <<<EOH\n<input type=\"radio\" name=\"newsletter\" value=\"accept\" checked=\"checked\"  />\n\nEOH;\n\n\t\t$this->assertEquals($expected, form_radio('newsletter', 'accept', TRUE));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_form_submit()\n\t{\n\t\t$expected = <<<EOH\n<input type=\"submit\" name=\"mysubmit\" value=\"Submit Post!\"  />\n\nEOH;\n\n\t\t$this->assertEquals($expected, form_submit('mysubmit', 'Submit Post!'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_form_label()\n\t{\n\t\t$expected = <<<EOH\n<label for=\"username\">What is your Name</label>\nEOH;\n\n\t\t$this->assertEquals($expected, form_label('What is your Name', 'username'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_form_reset()\n\t{\n\t\t$expected = <<<EOH\n<input type=\"reset\" name=\"myreset\" value=\"Reset\"  />\n\nEOH;\n\n\t\t$this->assertEquals($expected, form_reset('myreset', 'Reset'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_form_button()\n\t{\n\t\t$expected = <<<EOH\n<button name=\"name\" type=\"button\" >content</button>\n\nEOH;\n\n\t\t$this->assertEquals($expected, form_button('name', 'content'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_form_close()\n\t{\n\t\t$expected = <<<EOH\n</form></div></div>\nEOH;\n\n\t\t$this->assertEquals($expected, form_close('</div></div>'));\n\t}\n}\n"
  },
  {
    "path": "tests/codeigniter/helpers/html_helper_test.php",
    "content": "<?php\n\nclass Html_helper_test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t$this->helper('html');\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_heading()\n\t{\n\t\t$this->assertEquals('<h1>foobar</h1>', heading('foobar'));\n\t\t$this->assertEquals('<h2 class=\"bar\">foobar</h2>', heading('foobar', 2, 'class=\"bar\"'));\n\t}\n\n\tpublic function test_heading_array_attributes()\n\t{\n\t\t// Test array of attributes\n\t\t$this->assertEquals('<h2 class=\"bar\" id=\"foo\">foobar</h2>', heading('foobar', 2, array('class' => 'bar', 'id' => 'foo')));\n\t}\n\n\tpublic function test_heading_object_attributes()\n\t{\n\t\t// Test array of attributes\n\t\t$this->assertEquals('<h2 class=\"bar\" id=\"foo\">foobar</h2>', heading('foobar', 2, array('class' => 'bar', 'id' => 'foo')));\n\t\t$test = new stdClass;\n\t\t$test->class = \"bar\";\n\t\t$test->id = \"foo\";\n\t\t$this->assertEquals('<h2 class=\"bar\" id=\"foo\">foobar</h2>', heading('foobar', 2, $test));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_img()\n\t{\n\t\t$this->ci_set_config('base_url', 'http://localhost/');\n\t\t$this->assertEquals('<img src=\"http://localhost/test\" alt=\"\" />', img(\"test\"));\n\t\t$this->assertEquals('<img src=\"data:foo/bar,baz\" alt=\"\" />', img(\"data:foo/bar,baz\"));\n\t\t$this->assertEquals('<img src=\"http://localhost/data://foo\" alt=\"\" />', img(\"data://foo\"));\n\t\t$this->assertEquals('<img src=\"//foo.bar/baz\" alt=\"\" />', img(\"//foo.bar/baz\"));\n\t\t$this->assertEquals('<img src=\"http://foo.bar/baz\" alt=\"\" />', img(\"http://foo.bar/baz\"));\n\t\t$this->assertEquals('<img src=\"https://foo.bar/baz\" alt=\"\" />', img(\"https://foo.bar/baz\"));\n\t\t$this->assertEquals('<img src=\"ftp://foo.bar/baz\" alt=\"\" />', img(\"ftp://foo.bar/baz\"));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_Ul()\n\t{\n\t\t$expect = <<<EOH\n<ul>\n  <li>foo</li>\n  <li>bar</li>\n</ul>\n\nEOH;\n\n\t\t$expect = ltrim($expect);\n\t\t$list = array('foo', 'bar');\n\n\t\t$this->assertEquals(ltrim($expect), ul($list));\n\n\t\t$expect = <<<EOH\n<ul class=\"test\">\n  <li>foo</li>\n  <li>bar</li>\n</ul>\n\nEOH;\n\n\t\t$expect = ltrim($expect);\n\n\t\t$this->assertEquals($expect, ul($list, 'class=\"test\"'));\n\n\t\t$this->assertEquals($expect, ul($list, array('class' => 'test')));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_meta()\n\t{\n\t\t$this->assertEquals(\n\t\t\t\"<meta name=\\\"test\\\" content=\\\"foo\\\" />\\n\",\n\t\t\tmeta('test', 'foo')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\t\"<meta name=\\\"foo\\\" content=\\\"\\\" />\\n\",\n\t\t\tmeta(array('name' => 'foo'))\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\t\"<meta charset=\\\"foo\\\" />\\n\",\n\t\t\tmeta(array('name' => 'foo', 'type' => 'charset'))\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\t\"<meta charset=\\\"foo\\\" />\\n\",\n\t\t\tmeta(array('name' => 'foo', 'type' => 'charset'))\n\t\t);\n\t}\n}\n"
  },
  {
    "path": "tests/codeigniter/helpers/inflector_helper_test.php",
    "content": "<?php\n\nclass Inflector_helper_test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t$this->helper('inflector');\n\t}\n\n\tpublic function test_singular()\n\t{\n\t\t$strs = array(\n\t\t\t'tellies'      => 'telly',\n\t\t\t'smellies'     => 'smelly',\n\t\t\t'abjectnesses' => 'abjectness',\n\t\t\t'smells'       => 'smell',\n\t\t\t'equipment'    => 'equipment'\n\t\t);\n\n\t\tforeach ($strs as $str => $expect)\n\t\t{\n\t\t\t$this->assertEquals($expect, singular($str));\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_plural()\n\t{\n\t\t$strs = array(\n\t\t\t'telly'      => 'tellies',\n\t\t\t'smelly'     => 'smellies',\n\t\t\t'abjectness' => 'abjectnesses', // ref : https://en.wiktionary.org/wiki/abjectnesses\n\t\t\t'smell'      => 'smells',\n\t\t\t'witch'      => 'witches',\n\t\t\t'equipment'  => 'equipment'\n\t\t);\n\n\t\tforeach ($strs as $str => $expect)\n\t\t{\n\t\t\t$this->assertEquals($expect, plural($str));\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_camelize()\n\t{\n\t\t$strs = array(\n\t\t\t'this is the string'\t=> 'thisIsTheString',\n\t\t\t'this is another one'   => 'thisIsAnotherOne',\n\t\t\t'i-am-playing-a-trick'  => 'i-am-playing-a-trick',\n\t\t\t'what_do_you_think-yo?' => 'whatDoYouThink-yo?',\n\t\t);\n\n\t\tforeach ($strs as $str => $expect)\n\t\t{\n\t\t\t$this->assertEquals($expect, camelize($str));\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_underscore()\n\t{\n\t\t$strs = array(\n\t\t\t'this is the string'    => 'this_is_the_string',\n\t\t\t'this is another one'   => 'this_is_another_one',\n\t\t\t'i-am-playing-a-trick'  => 'i-am-playing-a-trick',\n\t\t\t'what_do_you_think-yo?' => 'what_do_you_think-yo?',\n\t\t);\n\n\t\tforeach ($strs as $str => $expect)\n\t\t{\n\t\t\t$this->assertEquals($expect, underscore($str));\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_humanize()\n\t{\n\t\t$strs = array(\n\t\t\t'this_is_the_string'    => 'This Is The String',\n\t\t\t'this_is_another_one'   => 'This Is Another One',\n\t\t\t'i-am-playing-a-trick'  => 'I-am-playing-a-trick',\n\t\t\t'what_do_you_think-yo?' => 'What Do You Think-yo?',\n\t\t);\n\n\t\tforeach ($strs as $str => $expect)\n\t\t{\n\t\t\t$this->assertEquals($expect, humanize($str));\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_ordinal_format()\n\t{\n\t\t$strs = array(\n\t\t\t1                => '1st',\n\t\t\t2                => '2nd',\n\t\t\t4                => '4th',\n\t\t\t11               => '11th',\n\t\t\t12               => '12th',\n\t\t\t13               => '13th',\n\t\t\t'something else' => 'something else',\n\t\t);\n\n\t\tforeach ($strs as $str => $expect)\n\t\t{\n\t\t\t$this->assertEquals($expect, ordinal_format($str));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tests/codeigniter/helpers/language_helper_test.php",
    "content": "<?php\n\nclass Language_helper_test extends CI_TestCase {\n\n\tpublic function test_lang()\n\t{\n\t\t$this->helper('language');\n\t\t$lang = $this->getMockBuilder('CI_Lang')->setMethods(array('line'))->getMock();\n\t\t$lang->expects($this->any())->method('line')->will($this->returnValue(FALSE));\n\t\t$this->ci_instance_var('lang', $lang);\n\n\t\t$this->assertFalse(lang(1));\n\t\t$this->assertEquals('<label for=\"foo\" class=\"bar\"></label>', lang(1, 'foo', array('class' => 'bar')));\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/helpers/number_helper_test.php",
    "content": "<?php\n\nclass Number_helper_test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t$this->helper('number');\n\n\t\t// Grab the core lang class\n\t\t$lang_cls = $this->ci_core_class('lang');\n\n\t\t// Mock away load, too much going on in there,\n\t\t// we'll just check for the expected parameter\n\t\t$lang = $this->getMockBuilder('CI_Lang')->setMethods(array('load'))->getMock();\n\t\t$lang->expects($this->once())\n\t\t\t ->method('load')\n\t\t\t ->with($this->equalTo('number'));\n\n\t\t// Assign the proper language array\n\t\t$lang->language = $this->lang('number');\n\n\t\t// We don't have a controller, so just create\n\t\t// a cheap class to act as our super object.\n\t\t// Make sure it has a lang attribute.\n\t\t$this->ci_instance_var('lang', $lang);\n\t}\n\n\tpublic function test_byte_format()\n\t{\n\t\t$this->assertEquals('456 Bytes', byte_format(456));\n\t}\n\n\tpublic function test_kb_format()\n\t{\n\t\t$this->assertEquals('4.5 KB', byte_format(4567));\n\t}\n\n\tpublic function test_kb_format_medium()\n\t{\n\t\t$this->assertEquals('44.6 KB', byte_format(45678));\n\t}\n\n\tpublic function test_kb_format_large()\n\t{\n\t\t$this->assertEquals('446.1 KB', byte_format(456789));\n\t}\n\n\tpublic function test_mb_format()\n\t{\n\t\t$this->assertEquals('3.3 MB', byte_format(3456789));\n\t}\n\n\tpublic function test_gb_format()\n\t{\n\t\t$this->assertEquals('1.8 GB', byte_format(1932735283.2));\n\t}\n\n\tpublic function test_tb_format()\n\t{\n\t\t$this->assertEquals('112,283.3 TB', byte_format(123456789123456789));\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/helpers/path_helper_test.php",
    "content": "<?php\n\nclass Path_helper_test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t$this->helper('path');\n\t}\n\n\tpublic function test_set_realpath()\n\t{\n\t\t$this->assertEquals(getcwd().DIRECTORY_SEPARATOR, set_realpath(getcwd()));\n\t}\n\n\tpublic function test_set_realpath_nonexistent_directory()\n\t{\n\t\t$expected = '/path/to/nowhere';\n\t\t$this->assertEquals($expected, set_realpath('/path/to/nowhere', FALSE));\n\t}\n\n\tpublic function test_set_realpath_error_trigger()\n\t{\n\t\t$this->setExpectedException(\n\t\t\t\t'RuntimeException', 'CI Error: Not a valid path: /path/to/nowhere'\n\t\t);\n\n\t\tset_realpath('/path/to/nowhere', TRUE);\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/helpers/security_helper_test.php",
    "content": "<?php\n\nclass Security_helper_tests extends CI_TestCase {\n\n\tfunction setUp()\n\t{\n\t\t$this->helper('security');\n\t\t$obj = new stdClass;\n\t\t$obj->security = new Mock_Core_Security('UTF-8');\n\t\t$this->ci_instance($obj);\n\t}\n\n\tfunction test_xss_clean()\n\t{\n\t\t$this->assertEquals('foo', xss_clean('foo'));\n\n\t\t$this->assertEquals(\"Hello, i try to [removed]alert&#40;'Hack'&#41;;[removed] your site\", xss_clean(\"Hello, i try to <script>alert('Hack');</script> your site\"));\n\t}\n\n\tfunction test_sanitize_filename()\n\t{\n\t\t$this->assertEquals('hello.doc', sanitize_filename('hello.doc'));\n\n\t\t$filename = './<!--foo-->';\n\t\t$this->assertEquals('foo', sanitize_filename($filename));\n\t}\n\n\tfunction test_strip_image_tags()\n\t{\n\t\t$this->assertEquals('http://example.com/spacer.gif', strip_image_tags('http://example.com/spacer.gif'));\n\n\t\t$this->assertEquals('http://example.com/spacer.gif', strip_image_tags('<img src=\"http://example.com/spacer.gif\" alt=\"Who needs CSS when you have a spacer.gif?\" />'));\n\t}\n\n\tfunction test_encode_php_tags()\n\t{\n\t\t$this->assertEquals('&lt;? echo $foo; ?&gt;', encode_php_tags('<? echo $foo; ?>'));\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/helpers/string_helper_test.php",
    "content": "<?php\n\nclass String_helper_test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t$this->helper('string');\n\t}\n\n\tpublic function test_strip_slashes()\n\t{\n\t\t$expected = array(\n\t\t\t\"Is your name O'reilly?\",\n\t\t\t\"No, my name is O'connor.\"\n\t\t);\n\n\t\t$str = array(\n\t\t\t\"Is your name O\\'reilly?\",\n\t\t\t\"No, my name is O\\'connor.\"\n\t\t);\n\n\t\t$this->assertEquals($expected, strip_slashes($str));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_strip_quotes()\n\t{\n\t\t$strs = array(\n\t\t\t'\"me oh my!\"'\t\t=> 'me oh my!',\n\t\t\t\"it's a winner!\"\t=> 'its a winner!',\n\t\t);\n\n\t\tforeach ($strs as $str => $expect)\n\t\t{\n\t\t\t$this->assertEquals($expect, strip_quotes($str));\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_quotes_to_entities()\n\t{\n\t\t$strs = array(\n\t\t\t'\"me oh my!\"'\t\t=> '&quot;me oh my!&quot;',\n\t\t\t\"it's a winner!\"\t=> 'it&#39;s a winner!',\n\t\t);\n\n\t\tforeach ($strs as $str => $expect)\n\t\t{\n\t\t\t$this->assertEquals($expect, quotes_to_entities($str));\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_reduce_double_slashes()\n\t{\n\t\t$strs = array(\n\t\t\t'http://codeigniter.com'\t\t=> 'http://codeigniter.com',\n\t\t\t'//var/www/html/example.com/'\t=> '/var/www/html/example.com/',\n\t\t\t'/var/www/html//index.php'\t\t=> '/var/www/html/index.php'\n\t\t);\n\n\t\tforeach ($strs as $str => $expect)\n\t\t{\n\t\t\t$this->assertEquals($expect, reduce_double_slashes($str));\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_reduce_multiples()\n\t{\n\t\t$strs = array(\n\t\t\t'Fred, Bill,, Joe, Jimmy'\t=> 'Fred, Bill, Joe, Jimmy',\n\t\t\t'Ringo, John, Paul,,'\t\t=> 'Ringo, John, Paul,'\n\t\t);\n\n\t\tforeach ($strs as $str => $expect)\n\t\t{\n\t\t\t$this->assertEquals($expect, reduce_multiples($str));\n\t\t}\n\n\t\t$strs = array(\n\t\t\t'Fred, Bill,, Joe, Jimmy'\t=> 'Fred, Bill, Joe, Jimmy',\n\t\t\t'Ringo, John, Paul,,'\t\t=> 'Ringo, John, Paul'\n\t\t);\n\n\t\tforeach ($strs as $str => $expect)\n\t\t{\n\t\t\t$this->assertEquals($expect, reduce_multiples($str, ',', TRUE));\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_random_string()\n\t{\n\t\t$this->assertEquals(16, strlen(random_string('alnum', 16)));\n\t\t$this->assertEquals(32, strlen(random_string('md5', 16)));\n\t\t$this->assertEquals('string', gettype(random_string('numeric', 16)));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_increment_string()\n\t{\n\t\t$this->assertEquals('my-test_1', increment_string('my-test'));\n\t\t$this->assertEquals('my-test-1', increment_string('my-test', '-'));\n\t\t$this->assertEquals('file_5', increment_string('file_4'));\n\t\t$this->assertEquals('file-5', increment_string('file-4', '-'));\n\t\t$this->assertEquals('file-5', increment_string('file-4', '-'));\n\t\t$this->assertEquals('file-1', increment_string('file', '-', '1'));\n\t\t$this->assertEquals(124, increment_string('123', ''));\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/helpers/text_helper_test.php",
    "content": "<?php\n\nclass Text_helper_test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t$this->helper('text');\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_word_limiter()\n\t{\n\t\t$long_string = 'Once upon a time, a framework had no tests.  It sad.  So some nice people began to write tests.  The more time that went on, the happier it became.  Everyone was happy.';\n\n\t\t$this->assertEquals('Once upon a time,&#8230;', word_limiter($long_string, 4));\n\t\t$this->assertEquals('Once upon a time,&hellip;', word_limiter($long_string, 4, '&hellip;'));\n\t\t$this->assertEquals('', word_limiter('', 4));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_character_limiter()\n\t{\n\t\t$long_string = 'Once upon a time, a framework had no tests.  It sad.  So some nice people began to write tests.  The more time that went on, the happier it became.  Everyone was happy.';\n\n\t\t$this->assertEquals('Once upon a time, a&#8230;', character_limiter($long_string, 20));\n\t\t$this->assertEquals('Once upon a time, a&hellip;', character_limiter($long_string, 20, '&hellip;'));\n\t\t$this->assertEquals('Short', character_limiter('Short', 20));\n\t\t$this->assertEquals('Short', character_limiter('Short', 5));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_ascii_to_entities()\n\t{\n\t\t$strs = array(\n\t\t\t'“‘ “test”'\t\t\t=> '&#8220;&#8216; &#8220;test&#8221;',\n\t\t\t'†¥¨ˆøåß∂ƒ©˙∆˚¬'\t=> '&#8224;&#165;&#168;&#710;&#248;&#229;&#223;&#8706;&#402;&#169;&#729;&#8710;&#730;&#172;'\n\t\t);\n\n\t\tforeach ($strs as $str => $expect)\n\t\t{\n\t\t\t$this->assertEquals($expect, ascii_to_entities($str));\n\t\t}\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_entities_to_ascii()\n\t{\n\t\t$strs = array(\n\t\t\t'&#8220;&#8216; &#8220;test&#8221;' => '“‘ “test”',\n\t\t\t'&#8224;&#165;&#168;&#710;&#248;&#229;&#223;&#8706;&#402;&#169;&#729;&#8710;&#730;&#172;' => '†¥¨ˆøåß∂ƒ©˙∆˚¬'\n\t\t);\n\n\t\tforeach ($strs as $str => $expect)\n\t\t{\n\t\t\t$this->assertEquals($expect, entities_to_ascii($str));\n\t\t}\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_convert_accented_characters()\n\t{\n\t\tif (substr(PHP_VERSION, 0, 3) === '7.4')\n\t\t{\n\t\t\treturn $this->markTestSkipped('For some reason all PHP 7.4 instances on GitHub Actions trigger a parse error when foreign_chars.php is loaded');\n\t\t}\n\n\t\t$this->ci_vfs_clone('application/config/foreign_chars.php');\n\t\t$this->assertEquals('AAAeEEEIIOOEUUUeY', convert_accented_characters('ÀÂÄÈÊËÎÏÔŒÙÛÜŸ'));\n\t\t$this->assertEquals('a e i o u n ue', convert_accented_characters('á é í ó ú ñ ü'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_censored_words()\n\t{\n\t\t$censored = array('boob', 'nerd', 'ass', 'fart');\n\n\t\t$strs = array(\n\t\t\t'Ted bobbled the ball' \t\t\t=> 'Ted bobbled the ball',\n\t\t\t'Jake is a nerdo'\t\t\t\t=> 'Jake is a nerdo',\n\t\t\t'The borg will assimilate you'\t=> 'The borg will assimilate you',\n\t\t\t'Did Mary Fart?'\t\t\t\t=> 'Did Mary $*#?',\n\t\t\t'Jake is really a boob'\t\t\t=> 'Jake is really a $*#'\n\t\t);\n\n\t\tforeach ($strs as $str => $expect)\n\t\t{\n\t\t\t$this->assertEquals($expect, word_censor($str, $censored, '$*#'));\n\t\t}\n\n\t\t// test censored words being sent as a string\n\t\t$this->assertEquals('test', word_censor('test', 'test'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_highlight_code()\n\t{\n\t\t$expect = \"<code><span style=\\\"color: #000000\\\">\\n<span style=\\\"color: #0000BB\\\">&lt;?php&nbsp;var_dump</span><span style=\\\"color: #007700\\\">(</span><span style=\\\"color: #0000BB\\\">\\$this</span><span style=\\\"color: #007700\\\">);&nbsp;</span><span style=\\\"color: #0000BB\\\">?&gt;&nbsp;</span>\\n</span>\\n</code>\";\n\n\t\t$this->assertEquals($expect, highlight_code('<?php var_dump($this); ?>'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * @runInSeparateProcess\n\t */\n\tpublic function test_highlight_phrase()\n\t{\n\t\tdefine('UTF8_ENABLED', FALSE);\n\n\t\t$strs = array(\n\t\t\t'this is a phrase'          => '<mark>this is</mark> a phrase',\n\t\t\t'this is another'           => '<mark>this is</mark> another',\n\t\t\t'Gimme a test, Sally'       => 'Gimme a test, Sally',\n\t\t\t'Or tell me what this is'   => 'Or tell me what <mark>this is</mark>',\n\t\t\t''                          => ''\n\t\t);\n\n\t\tforeach ($strs as $str => $expect)\n\t\t{\n\t\t\t$this->assertEquals($expect, highlight_phrase($str, 'this is'));\n\t\t}\n\n\t\t$this->assertEquals('<strong>this is</strong> a strong test', highlight_phrase('this is a strong test', 'this is', '<strong>', '</strong>'));\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_ellipsize()\n\t{\n\t\t$strs = array(\n\t\t\t'0'\t\t=> array(\n\t\t\t\t'this is my string'\t\t\t\t=> '&hellip; my string',\n\t\t\t\t\"here's another one\"\t\t\t=> '&hellip;nother one',\n\t\t\t\t'this one is just a bit longer'\t=> '&hellip;bit longer',\n\t\t\t\t'short'\t\t\t\t\t\t\t=> 'short'\n\t\t\t),\n\t\t\t'.5'\t=> array(\n\t\t\t\t'this is my string'\t\t\t\t=> 'this &hellip;tring',\n\t\t\t\t\"here's another one\"\t\t\t=> \"here'&hellip;r one\",\n\t\t\t\t'this one is just a bit longer'\t=> 'this &hellip;onger',\n\t\t\t\t'short'\t\t\t\t\t\t\t=> 'short'\n\t\t\t),\n\t\t\t'1'\t=> array(\n\t\t\t\t'this is my string'\t\t\t\t=> 'this is my&hellip;',\n\t\t\t\t\"here's another one\"\t\t\t=> \"here's ano&hellip;\",\n\t\t\t\t'this one is just a bit longer'\t=> 'this one i&hellip;',\n\t\t\t\t'short'\t\t\t\t\t\t\t=> 'short'\n\t\t\t),\n\t\t);\n\n\t\tforeach ($strs as $pos => $s)\n\t\t{\n\t\t\tforeach ($s as $str => $expect)\n\t\t\t{\n\t\t\t\t$this->assertEquals($expect, ellipsize($str, 10, $pos));\n\t\t\t}\n\t\t}\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_word_wrap()\n\t{\n\t\t$string = 'Here is a simple string of text that will help us demonstrate this function.';\n\t\t$this->assertEquals(substr_count(word_wrap($string, 25), \"\\n\"), 4);\n\t}\n\n\t// ------------------------------------------------------------------------\n\n\tpublic function test_default_word_wrap_charlim()\n\t{\n\t\t$string = \"Here is a longer string of text that will help us demonstrate the default charlim of this function.\";\n\t\t$this->assertEquals(strpos(word_wrap($string), \"\\n\"), 73);\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/helpers/url_helper_test.php",
    "content": "<?php\n\nclass Url_helper_test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t$this->helper('url');\n\t}\n\n\t/**\n\t * @runInSeparateProcess\n\t */\n\tpublic function test_url_title()\n\t{\n\t\tdefine('UTF8_ENABLED', FALSE);\n\n\t\t$words = array(\n\t\t\t'foo bar /' \t=> 'foo-bar',\n\t\t\t'\\  testing 12' => 'testing-12'\n\t\t);\n\n\t\tforeach ($words as $in => $out)\n\t\t{\n\t\t\t$this->assertEquals($out, url_title($in, '-', TRUE));\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * @runInSeparateProcess\n\t */\n\tpublic function test_url_title_extra_dashes()\n\t{\n\t\tdefine('UTF8_ENABLED', FALSE);\n\n\t\t$words = array(\n\t\t\t'_foo bar_' \t=> 'foo_bar',\n\t\t\t'_What\\'s wrong with CSS?_' => 'Whats_wrong_with_CSS'\n\t\t);\n\n\t\tforeach ($words as $in => $out)\n\t\t{\n\t\t\t$this->assertEquals($out, url_title($in, '_'));\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_prep_url()\n\t{\n\t\t$this->assertEquals('http://codeigniter.com', prep_url('codeigniter.com'));\n\t\t$this->assertEquals('http://www.codeigniter.com', prep_url('www.codeigniter.com'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_auto_link_url()\n\t{\n\t\t$strings = array(\n\t\t\t'www.codeigniter.com test' => '<a href=\"http://www.codeigniter.com\">www.codeigniter.com</a> test',\n\t\t\t'This is my noreply@codeigniter.com test' => 'This is my noreply@codeigniter.com test',\n\t\t\t'<br />www.google.com' => '<br /><a href=\"http://www.google.com\">www.google.com</a>',\n\t\t\t'Download CodeIgniter at www.codeigniter.com. Period test.' => 'Download CodeIgniter at <a href=\"http://www.codeigniter.com\">www.codeigniter.com</a>. Period test.',\n\t\t\t'Download CodeIgniter at www.codeigniter.com, comma test' => 'Download CodeIgniter at <a href=\"http://www.codeigniter.com\">www.codeigniter.com</a>, comma test',\n\t\t\t'This one: ://codeigniter.com must not break this one: http://codeigniter.com' => 'This one: <a href=\"://codeigniter.com\">://codeigniter.com</a> must not break this one: <a href=\"http://codeigniter.com\">http://codeigniter.com</a>',\n\t\t\t'Trailing slash: https://codeigniter.com/ fubar' => 'Trailing slash: <a href=\"https://codeigniter.com/\">https://codeigniter.com/</a> fubar'\n\t\t);\n\n\t\tforeach ($strings as $in => $out)\n\t\t{\n\t\t\t$this->assertEquals($out, auto_link($in, 'url'));\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_pull_675()\n\t{\n\t\t$strings = array(\n\t\t\t'<br />www.google.com' => '<br /><a href=\"http://www.google.com\">www.google.com</a>',\n\t\t);\n\n\t\tforeach ($strings as $in => $out)\n\t\t{\n\t\t\t$this->assertEquals($out, auto_link($in, 'url'));\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_issue_5331()\n\t{\n\t\t$this->assertEquals(\n\t\t\t'this is some text that includes '.safe_mailto('www.email@domain.com').' which is causing an issue',\n\t\t\tauto_link('this is some text that includes www.email@domain.com which is causing an issue')\n\t\t);\n\t}\n}\n"
  },
  {
    "path": "tests/codeigniter/helpers/xml_helper_test.php",
    "content": "<?php\n\nclass Xml_helper_test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t$this->helper('xml');\n\t}\n\n\tpublic function test_xml_convert()\n\t{\n\t\t$this->assertEquals('&lt;tag&gt;my &amp; test &#45; &lt;/tag&gt;', xml_convert('<tag>my & test - </tag>'));\n\t}\n\n}"
  },
  {
    "path": "tests/codeigniter/libraries/Calendar_test.php",
    "content": "<?php\n\nclass Calendar_test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t// Required for get_total_days()\n\t\t$this->ci_instance_var('load', $this->getMockBuilder('CI_Loader')->setMethods(array('helper'))->getMock());\n\n\t\t$lang = $this->getMockBuilder('CI_Lang')->setMethods(array('load', 'line'))->getMock();\n\t\t$lang->expects($this->any())->method('line')->will($this->returnValue(FALSE));\n\t\t$this->ci_instance_var('lang', $lang);\n\n\t\t$this->calendar = new CI_Calendar();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_initialize()\n\t{\n\t\t$this->calendar->initialize(array(\n\t\t\t'month_type'\t=>\t'short',\n\t\t\t'start_day'\t=>\t'monday'\n\t\t));\n\t\t$this->assertEquals('short', $this->calendar->month_type);\n\t\t$this->assertEquals('monday', $this->calendar->start_day);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_generate()\n\t{\n\t\t$no_events = '<table border=\"0\" cellpadding=\"4\" cellspacing=\"0\">\n\n<tr>\n<th colspan=\"7\">September&nbsp;2011</th>\n\n</tr>\n\n<tr>\n<td>Su</td><td>Mo</td><td>Tu</td><td>We</td><td>Th</td><td>Fr</td><td>Sa</td>\n</tr>\n\n<tr>\n<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>1</td><td>2</td><td>3</td>\n</tr>\n\n<tr>\n<td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td><td>10</td>\n</tr>\n\n<tr>\n<td>11</td><td>12</td><td>13</td><td>14</td><td>15</td><td>16</td><td>17</td>\n</tr>\n\n<tr>\n<td>18</td><td>19</td><td>20</td><td>21</td><td>22</td><td>23</td><td>24</td>\n</tr>\n\n<tr>\n<td>25</td><td>26</td><td>27</td><td>28</td><td>29</td><td>30</td><td>&nbsp;</td>\n</tr>\n\n</table>';\n\n\t\t$this->assertEquals($no_events, $this->calendar->generate(2011, 9));\n\n\t\t$data = array(\n\t\t\t3  => 'http://example.com/news/article/2006/03/',\n\t\t\t7  => 'http://example.com/news/article/2006/07/',\n\t\t\t13 => 'http://example.com/news/article/2006/13/',\n\t\t\t26 => 'http://example.com/news/article/2006/26/'\n\t\t);\n\n\t\t$events = '<table border=\"0\" cellpadding=\"4\" cellspacing=\"0\">\n\n<tr>\n<th colspan=\"7\">September&nbsp;2011</th>\n\n</tr>\n\n<tr>\n<td>Su</td><td>Mo</td><td>Tu</td><td>We</td><td>Th</td><td>Fr</td><td>Sa</td>\n</tr>\n\n<tr>\n<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>1</td><td>2</td><td><a href=\"http://example.com/news/article/2006/03/\">3</a></td>\n</tr>\n\n<tr>\n<td>4</td><td>5</td><td>6</td><td><a href=\"http://example.com/news/article/2006/07/\">7</a></td><td>8</td><td>9</td><td>10</td>\n</tr>\n\n<tr>\n<td>11</td><td>12</td><td><a href=\"http://example.com/news/article/2006/13/\">13</a></td><td>14</td><td>15</td><td>16</td><td>17</td>\n</tr>\n\n<tr>\n<td>18</td><td>19</td><td>20</td><td>21</td><td>22</td><td>23</td><td>24</td>\n</tr>\n\n<tr>\n<td>25</td><td><a href=\"http://example.com/news/article/2006/26/\">26</a></td><td>27</td><td>28</td><td>29</td><td>30</td><td>&nbsp;</td>\n</tr>\n\n</table>';\n\n\t\t$this->assertEquals($events, $this->calendar->generate(2011, 9, $data));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_get_month_name()\n\t{\n\t\t$this->calendar->month_type = NULL;\n\t\t$this->assertEquals('January', $this->calendar->get_month_name('01'));\n\n\t\t$this->calendar->month_type = 'short';\n\t\t$this->assertEquals('Jan', $this->calendar->get_month_name('01'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_get_day_names()\n\t{\n\t\t$this->assertEquals(array(\n\t\t\t'Sunday',\n\t\t\t'Monday',\n\t\t\t'Tuesday',\n\t\t\t'Wednesday',\n\t\t\t'Thursday',\n\t\t\t'Friday',\n\t\t\t'Saturday'\n\t\t), $this->calendar->get_day_names('long'));\n\n\t\t$this->assertEquals(array(\n\t\t\t'Sun',\n\t\t\t'Mon',\n\t\t\t'Tue',\n\t\t\t'Wed',\n\t\t\t'Thu',\n\t\t\t'Fri',\n\t\t\t'Sat'\n\t\t), $this->calendar->get_day_names('short'));\n\n\t\t$this->calendar->day_type = NULL;\n\n\t\t$this->assertEquals(array(\n\t\t\t'Su',\n\t\t\t'Mo',\n\t\t\t'Tu',\n\t\t\t'We',\n\t\t\t'Th',\n\t\t\t'Fr',\n\t\t\t'Sa'\n\t\t), $this->calendar->get_day_names());\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_adjust_date()\n\t{\n\t\t$this->assertEquals(array('month' => 8, 'year' => 2012), $this->calendar->adjust_date(8, 2012));\n\t\t$this->assertEquals(array('month' => 1, 'year' => 2013), $this->calendar->adjust_date(13, 2012));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_get_total_days()\n\t{\n\t\t$this->assertEquals(0, $this->calendar->get_total_days(13, 2012));\n\n\t\t$this->assertEquals(31, $this->calendar->get_total_days(1, 2012));\n\t\t$this->assertEquals(28, $this->calendar->get_total_days(2, 2011));\n\t\t$this->assertEquals(29, $this->calendar->get_total_days(2, 2012));\n\t\t$this->assertEquals(31, $this->calendar->get_total_days(3, 2012));\n\t\t$this->assertEquals(30, $this->calendar->get_total_days(4, 2012));\n\t\t$this->assertEquals(31, $this->calendar->get_total_days(5, 2012));\n\t\t$this->assertEquals(30, $this->calendar->get_total_days(6, 2012));\n\t\t$this->assertEquals(31, $this->calendar->get_total_days(7, 2012));\n\t\t$this->assertEquals(31, $this->calendar->get_total_days(8, 2012));\n\t\t$this->assertEquals(30, $this->calendar->get_total_days(9, 2012));\n\t\t$this->assertEquals(31, $this->calendar->get_total_days(10, 2012));\n\t\t$this->assertEquals(30, $this->calendar->get_total_days(11, 2012));\n\t\t$this->assertEquals(31, $this->calendar->get_total_days(12, 2012));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_default_template()\n\t{\n\t\t$array = array(\n\t\t\t'table_open'\t\t\t=> '<table border=\"0\" cellpadding=\"4\" cellspacing=\"0\">',\n\t\t\t'heading_row_start'\t\t=> '<tr>',\n\t\t\t'heading_previous_cell'\t\t=> '<th><a href=\"{previous_url}\">&lt;&lt;</a></th>',\n\t\t\t'heading_title_cell'\t\t=> '<th colspan=\"{colspan}\">{heading}</th>',\n\t\t\t'heading_next_cell'\t\t=> '<th><a href=\"{next_url}\">&gt;&gt;</a></th>',\n\t\t\t'heading_row_end'\t\t=> '</tr>',\n\t\t\t'week_row_start'\t\t=> '<tr>',\n\t\t\t'week_day_cell'\t\t\t=> '<td>{week_day}</td>',\n\t\t\t'week_row_end'\t\t\t=> '</tr>',\n\t\t\t'cal_row_start'\t\t\t=> '<tr>',\n\t\t\t'cal_cell_start'\t\t=> '<td>',\n\t\t\t'cal_cell_start_today'\t\t=> '<td>',\n\t\t\t'cal_cell_content'\t\t=> '<a href=\"{content}\">{day}</a>',\n\t\t\t'cal_cell_content_today'\t=> '<a href=\"{content}\"><strong>{day}</strong></a>',\n\t\t\t'cal_cell_no_content'\t\t=> '{day}',\n\t\t\t'cal_cell_no_content_today'\t=> '<strong>{day}</strong>',\n\t\t\t'cal_cell_blank'\t\t=> '&nbsp;',\n\t\t\t'cal_cell_end'\t\t\t=> '</td>',\n\t\t\t'cal_cell_end_today'\t\t=> '</td>',\n\t\t\t'cal_row_end'\t\t\t=> '</tr>',\n\t\t\t'table_close'\t\t\t=> '</table>',\n\t\t\t'cal_cell_start_other'\t\t=> '<td style=\"color: #666;\">',\n\t\t\t'cal_cell_other'\t\t=> '{day}',\n\t\t\t'cal_cell_end_other'\t\t=> '</td>'\n\t\t);\n\n\t\t$this->assertEquals($array, $this->calendar->default_template());\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/libraries/Driver_test.php",
    "content": "<?php\n\n/**\n * Driver library base class unit test\n */\nclass Driver_test extends CI_TestCase {\n\n\tprivate $name;\n\n\t/**\n\t * Set up test framework\n\t */\n\tpublic function set_up()\n\t{\n\t\t// Set our subclass prefix\n\t\t$this->subclass = 'Mock_Libraries_';\n\t\t$this->ci_set_config('subclass_prefix', $this->subclass);\n\n\t\t// Mock Loader->get_package_paths\n\t\t$paths = 'get_package_paths';\n\t\t$ldr = $this->getMockBuilder('CI_Loader')->setMethods(array($paths))->getMock();\n\t\t$ldr->expects($this->any())->method($paths)->will($this->returnValue(array(APPPATH, BASEPATH)));\n\t\t$this->ci_instance_var('load', $ldr);\n\n\t\t// Create mock driver library\n\t\t$this->name = 'Driver';\n\t\t$this->lib = new Mock_Libraries_Driver();\n\t}\n\n\t/**\n\t * Test driver child loading\n\t */\n\tpublic function test_load_driver()\n\t{\n\t\t// Create driver file\n\t\t$driver = 'basic';\n\t\t$file = $this->name.'_'.$driver;\n\t\t$class = 'CI_'.$file;\n\t\t$prop = 'called';\n\t\t$content = '<?php class '.$class.' extends CI_Driver { public $'.$prop.' = FALSE; '.\n\t\t\t'public function decorate($parent) { $this->'.$prop.' = TRUE; } }';\n\t\t$this->ci_vfs_create($file, $content, $this->ci_base_root, array('libraries', $this->name, 'drivers'));\n\n\t\t// Make driver valid\n\t\t$this->lib->driver_list($driver);\n\n\t\t// Load driver\n\t\t$this->assertNotNull($this->lib->load_driver($driver));\n\n\t\t// Did lib name get set?\n\t\t$this->assertEquals($this->name, $this->lib->get_name());\n\n\t\t// Was driver loaded?\n\t\t$this->assertObjectHasAttribute($driver, $this->lib);\n\t\t$this->assertInstanceOf($class, $this->lib->$driver);\n\t\t$this->assertInstanceOf('CI_Driver', $this->lib->$driver);\n\n\t\t// Was decorate called?\n\t\t$this->assertObjectHasAttribute($prop, $this->lib->$driver);\n\t\t$this->assertTrue($this->lib->$driver->$prop);\n\n\t\t// Do we get an error for an invalid driver?\n\t\t$driver = 'unlisted';\n\t\t$this->setExpectedException('RuntimeException', 'CI Error: Invalid driver requested: '.$this->name.'_'.$driver);\n\t\t$this->lib->load_driver($driver);\n\t}\n\n\t/**\n\t * Test loading lowercase from app path\n\t */\n\tpublic function test_load_app_driver()\n\t{\n\t\t// Create driver file\n\t\t$driver = 'lowpack';\n\t\t$file = $this->name.'_'.$driver;\n\t\t$class = 'CI_'.$file;\n\t\t$content = '<?php class '.$class.' extends CI_Driver {  }';\n\t\t$this->ci_vfs_create($file, $content, $this->ci_app_root,\n\t\t\tarray('libraries', $this->name, 'drivers'));\n\n\t\t// Make valid list\n\t\t$nodriver = 'absent';\n\t\t$this->lib->driver_list(array($driver, $nodriver));\n\n\t\t// Load driver\n\t\t$this->assertNotNull($this->lib->load_driver($driver));\n\n\t\t// Was driver loaded?\n\t\t$this->assertObjectHasAttribute($driver, $this->lib);\n\t\t$this->assertInstanceOf($class, $this->lib->$driver);\n\t\t$this->assertInstanceOf('CI_Driver', $this->lib->$driver);\n\n\t\t// Do we get an error for a non-existent driver?\n\t\t$this->setExpectedException('RuntimeException', 'CI Error: Unable to load the requested driver: CI_'.\n\t\t\t$this->name.'_'.$nodriver);\n\t\t$this->lib->load_driver($nodriver);\n\t}\n\n\t/**\n\t * Test loading driver extension\n\t */\n\tpublic function test_load_driver_ext()\n\t{\n\t\t// Create base file\n\t\t$driver = 'extend';\n\t\t$base = $this->name.'_'.$driver;\n\t\t$baseclass = 'CI_'.$base;\n\t\t$content = '<?php class '.$baseclass.' extends CI_Driver {  }';\n\t\t$this->ci_vfs_create($base, $content, $this->ci_base_root, array('libraries', $this->name, 'drivers'));\n\n\t\t// Create driver file\n\t\t$class = $this->subclass.$base;\n\t\t$content = '<?php class '.$class.' extends '.$baseclass.' {  }';\n\t\t$this->ci_vfs_create($class, $content, $this->ci_app_root, array('libraries', $this->name, 'drivers'));\n\n\t\t// Make valid list\n\t\t$this->lib->driver_list($driver);\n\n\t\t// Load driver\n\t\t$this->assertNotNull($this->lib->load_driver($driver));\n\n\t\t// Was driver loaded?\n\t\t$this->assertObjectHasAttribute($driver, $this->lib);\n\t\t$this->assertInstanceOf($class, $this->lib->$driver);\n\t\t$this->assertInstanceOf($baseclass, $this->lib->$driver);\n\t\t$this->assertInstanceOf('CI_Driver', $this->lib->$driver);\n\n\t\t// Create driver extension without base\n\t\t$driver = 'baseless';\n\t\t$base = $this->name.'_'.$driver;\n\t\t$class = $this->subclass.$base;\n\t\t$content = '<?php class '.$class.' extends CI_Driver {  }';\n\t\t$this->ci_vfs_create($class, $content, $this->ci_app_root, array('libraries', $this->name, 'drivers'));\n\t\t$this->lib->driver_list($driver);\n\n\t\t// Do we get an error when base class isn't found?\n\t\t$this->setExpectedException('RuntimeException', 'CI Error: Unable to load the requested class: CI_'.$base);\n\t\t$this->lib->load_driver($driver);\n\t}\n\n\t/**\n\t * Test decorating driver with parent attributes\n\t */\n\tpublic function test_decorate()\n\t{\n\t\t// Create parent with a method and property to access\n\t\t$pclass = 'Test_Parent';\n\t\t$prop = 'parent_prop';\n\t\t$value = 'initial';\n\t\t$method = 'parent_func';\n\t\t$return = 'func return';\n\t\t$code = 'class '.$pclass.' { public $'.$prop.' = \\''.$value.'\\'; '.\n\t\t\t'public function '.$method.'() { return \\''.$return.'\\'; } }';\n\t\teval($code);\n\t\t$parent = new $pclass();\n\n\t\t// Create child driver to decorate\n\t\t$class = 'Test_Driver';\n\t\teval('class '.$class.' extends CI_Driver {  }');\n\t\t$child = new $class();\n\n\t\t// Decorate child\n\t\t$child->decorate($parent);\n\n\t\t// Do we get the initial parent property value?\n\t\t$this->assertEquals($value, $child->$prop);\n\n\t\t// Can we change the parent property?\n\t\t$newval = 'changed';\n\t\t$child->$prop = $newval;\n\t\t$this->assertEquals($newval, $parent->$prop);\n\n\t\t// Do we get back the updated value?\n\t\t$this->assertEquals($newval, $child->$prop);\n\n\t\t// Can we call the parent method?\n\t\t$this->assertEquals($return, $child->$method());\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/libraries/Encryption_test.php",
    "content": "<?php\n\nclass Encryption_test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t$this->encryption = new Mock_Libraries_Encryption();\n\n\t\tif (version_compare(PHP_VERSION, '7.1', '<'))\n\t\t{\n\t\t\t$this->markTestSkipped('Ubuntu-latest OpenSSL is not working correct in some older PHP versions.');\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * __construct test\n\t *\n\t * Covers behavior with $config['encryption_key'] set or not\n\t */\n\tpublic function test___construct()\n\t{\n\t\t// Assume no configuration from set_up()\n\t\t$this->assertNull($this->encryption->get_key());\n\n\t\t// Try with an empty value\n\t\t$this->ci_set_config('encryption_key');\n\t\t$this->encrypt = new Mock_Libraries_Encryption();\n\t\t$this->assertNull($this->encrypt->get_key());\n\n\t\t$this->ci_set_config('encryption_key', str_repeat(\"\\x0\", 16));\n\t\t$this->encrypt = new Mock_Libraries_Encryption();\n\t\t$this->assertEquals(str_repeat(\"\\x0\", 16), $this->encrypt->get_key());\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * hkdf() test\n\t *\n\t * Applies test vectors described in Appendix A(1-3) RFC5869.\n\t * Described vectors 4-7 SHA-1, which we don't support and are\n\t * therefore excluded.\n\t *\n\t * Because our implementation is a single method instead of being\n\t * split into hkdf_extract() and hkdf_expand(), we cannot test for\n\t * the PRK value. As long as the OKM is correct though, it's fine.\n\t *\n\t * @link\thttps://tools.ietf.org/rfc/rfc5869.txt\n\t */\n\tpublic function test_hkdf()\n\t{\n\t\t$vectors = array(\n\t\t\t// A.1: Basic test case with SHA-256\n\t\t\tarray(\n\t\t\t\t'digest' => 'sha256',\n\t\t\t\t'ikm' => \"\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\",\n\t\t\t\t'salt' => \"\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\",\n\t\t\t\t'length' => 42,\n\t\t\t\t'info' => \"\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9\",\n\t\t\t//\t'prk' => \"\\x07\\x77\\x09\\x36\\x2c\\x2e\\x32\\xdf\\x0d\\xdc\\x3f\\x0d\\xc4\\x7b\\xba\\x63\\x90\\xb6\\xc7\\x3b\\xb5\\x0f\\x9c\\x31\\x22\\xec\\x84\\x4a\\xd7\\xc2\\xb3\\xe5\",\n\t\t\t\t'okm' => \"\\x3c\\xb2\\x5f\\x25\\xfa\\xac\\xd5\\x7a\\x90\\x43\\x4f\\x64\\xd0\\x36\\x2f\\x2a\\x2d\\x2d\\x0a\\x90\\xcf\\x1a\\x5a\\x4c\\x5d\\xb0\\x2d\\x56\\xec\\xc4\\xc5\\xbf\\x34\\x00\\x72\\x08\\xd5\\xb8\\x87\\x18\\x58\\x65\"\n\t\t\t),\n\t\t\t// A.2: Test with SHA-256 and longer inputs/outputs\n\t\t\tarray(\n\t\t\t\t'digest' => 'sha256',\n\t\t\t\t'ikm' => \"\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f\\x20\\x21\\x22\\x23\\x24\\x25\\x26\\x27\\x28\\x29\\x2a\\x2b\\x2c\\x2d\\x2e\\x2f\\x30\\x31\\x32\\x33\\x34\\x35\\x36\\x37\\x38\\x39\\x3a\\x3b\\x3c\\x3d\\x3e\\x3f\\x40\\x41\\x42\\x43\\x44\\x45\\x46\\x47\\x48\\x49\\x4a\\x4b\\x4c\\x4d\\x4e\\x4f\",\n\t\t\t\t'salt' => \"\\x60\\x61\\x62\\x63\\x64\\x65\\x66\\x67\\x68\\x69\\x6a\\x6b\\x6c\\x6d\\x6e\\x6f\\x70\\x71\\x72\\x73\\x74\\x75\\x76\\x77\\x78\\x79\\x7a\\x7b\\x7c\\x7d\\x7e\\x7f\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89\\x8a\\x8b\\x8c\\x8d\\x8e\\x8f\\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97\\x98\\x99\\x9a\\x9b\\x9c\\x9d\\x9e\\x9f\\xa0\\xa1\\xa2\\xa3\\xa4\\xa5\\xa6\\xa7\\xa8\\xa9\\xaa\\xab\\xac\\xad\\xae\\xaf\",\n\t\t\t\t'length' => 82,\n\t\t\t\t'info' => \"\\xb0\\xb1\\xb2\\xb3\\xb4\\xb5\\xb6\\xb7\\xb8\\xb9\\xba\\xbb\\xbc\\xbd\\xbe\\xbf\\xc0\\xc1\\xc2\\xc3\\xc4\\xc5\\xc6\\xc7\\xc8\\xc9\\xca\\xcb\\xcc\\xcd\\xce\\xcf\\xd0\\xd1\\xd2\\xd3\\xd4\\xd5\\xd6\\xd7\\xd8\\xd9\\xda\\xdb\\xdc\\xdd\\xde\\xdf\\xe0\\xe1\\xe2\\xe3\\xe4\\xe5\\xe6\\xe7\\xe8\\xe9\\xea\\xeb\\xec\\xed\\xee\\xef\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9\\xfa\\xfb\\xfc\\xfd\\xfe\\xff\",\n\t\t\t//\t'prk' => \"\\x06\\xa6\\xb8\\x8c\\x58\\x53\\x36\\x1a\\x06\\x10\\x4c\\x9c\\xeb\\x35\\xb4\\x5c\\xef\\x76\\x00\\x14\\x90\\x46\\x71\\x01\\x4a\\x19\\x3f\\x40\\xc1\\x5f\\xc2\\x44\",\n\t\t\t\t'okm' => \"\\xb1\\x1e\\x39\\x8d\\xc8\\x03\\x27\\xa1\\xc8\\xe7\\xf7\\x8c\\x59\\x6a\\x49\\x34\\x4f\\x01\\x2e\\xda\\x2d\\x4e\\xfa\\xd8\\xa0\\x50\\xcc\\x4c\\x19\\xaf\\xa9\\x7c\\x59\\x04\\x5a\\x99\\xca\\xc7\\x82\\x72\\x71\\xcb\\x41\\xc6\\x5e\\x59\\x0e\\x09\\xda\\x32\\x75\\x60\\x0c\\x2f\\x09\\xb8\\x36\\x77\\x93\\xa9\\xac\\xa3\\xdb\\x71\\xcc\\x30\\xc5\\x81\\x79\\xec\\x3e\\x87\\xc1\\x4c\\x01\\xd5\\xc1\\xf3\\x43\\x4f\\x1d\\x87\",\n\t\t\t),\n\t\t\t// A.3: Test with SHA-256 and zero-length salt/info\n\t\t\tarray(\n\t\t\t\t'digest' => 'sha256',\n\t\t\t\t'ikm' => \"\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\",\n\t\t\t\t'salt' => '',\n\t\t\t\t'length' => 42,\n\t\t\t\t'info' => '',\n\t\t\t//\t'prk' => \"\\x19\\xef\\x24\\xa3\\x2c\\x71\\x7b\\x16\\x7f\\x33\\xa9\\x1d\\x6f\\x64\\x8b\\xdf\\x96\\x59\\x67\\x76\\xaf\\xdb\\x63\\x77\\xac\\x43\\x4c\\x1c\\x29\\x3c\\xcb\\x04\",\n\t\t\t\t'okm' => \"\\x8d\\xa4\\xe7\\x75\\xa5\\x63\\xc1\\x8f\\x71\\x5f\\x80\\x2a\\x06\\x3c\\x5a\\x31\\xb8\\xa1\\x1f\\x5c\\x5e\\xe1\\x87\\x9e\\xc3\\x45\\x4e\\x5f\\x3c\\x73\\x8d\\x2d\\x9d\\x20\\x13\\x95\\xfa\\xa4\\xb6\\x1a\\x96\\xc8\",\n\t\t\t)\n\t\t);\n\n\t\tforeach ($vectors as $test)\n\t\t{\n\t\t\t$this->assertEquals(\n\t\t\t\t$test['okm'],\n\t\t\t\t$this->encryption->hkdf(\n\t\t\t\t\t$test['ikm'],\n\t\t\t\t\t$test['digest'],\n\t\t\t\t\t$test['salt'],\n\t\t\t\t\t$test['length'],\n\t\t\t\t\t$test['info']\n\t\t\t\t)\n\t\t\t);\n\t\t}\n\n\t\t// Test default length, it must match the digest size\n\t\t$hkdf_result = $this->encryption->hkdf('foobar', 'sha512');\n\t\t$this->assertEquals(\n\t\t\t64,\n\t\t\tdefined('MB_OVERLOAD_STRING')\n\t\t\t\t? mb_strlen($hkdf_result, '8bit')\n\t\t\t\t: strlen($hkdf_result)\n\t\t);\n\n\t\t// Test maximum length (RFC5869 says that it must be up to 255 times the digest size)\n\t\t$hkdf_result = $this->encryption->hkdf('foobar', 'sha384', NULL, 48 * 255);\n\t\t$this->assertEquals(\n\t\t\t12240,\n\t\t\tdefined('MB_OVERLOAD_STRING')\n\t\t\t\t? mb_strlen($hkdf_result, '8bit')\n\t\t\t\t: strlen($hkdf_result)\n\t\t);\n\t\t$this->assertFalse($this->encryption->hkdf('foobar', 'sha224', NULL, 28 * 255 + 1));\n\n\t\t// CI-specific test for an invalid digest\n\t\t$this->assertFalse($this->encryption->hkdf('fobar', 'sha1'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * _get_params() test\n\t */\n\tpublic function test__get_params()\n\t{\n\t\t$key = str_repeat(\"\\x0\", 16);\n\n\t\t// Invalid custom parameters\n\t\t$params = array(\n\t\t\t// No cipher, mode or key\n\t\t\tarray('cipher' => 'aes-128', 'mode' => 'cbc'),\n\t\t\tarray('cipher' => 'aes-128', 'key' => $key),\n\t\t\tarray('mode' => 'cbc', 'key' => $key),\n\t\t\t// No HMAC key or not a valid digest\n\t\t\tarray('cipher' => 'aes-128', 'mode' => 'cbc', 'key' => $key),\n\t\t\tarray('cipher' => 'aes-128', 'mode' => 'cbc', 'key' => $key, 'hmac_digest' => 'sha1', 'hmac_key' => $key),\n\t\t\t// Invalid mode\n\t\t\tarray('cipher' => 'aes-128', 'mode' => 'foo', 'key' => $key, 'hmac_digest' => 'sha256', 'hmac_key' => $key)\n\t\t);\n\n\t\tfor ($i = 0, $c = count($params); $i < $c; $i++)\n\t\t{\n\t\t\t$this->assertFalse($this->encryption->__get_params($params[$i]));\n\t\t}\n\n\t\t// Valid parameters\n\t\t$params = array(\n\t\t\t'cipher' => 'aes-128',\n\t\t\t'mode' => 'cbc',\n\t\t\t'key' => str_repeat(\"\\x0\", 16),\n\t\t\t'hmac_key' => str_repeat(\"\\x0\", 16)\n\t\t);\n\n\t\t$this->assertEquals('array', gettype($this->encryption->__get_params($params)));\n\n\t\t$params['base64'] = TRUE;\n\t\t$params['hmac_digest'] = 'sha512';\n\n\t\t// Including all parameters\n\t\t$params = array(\n\t\t\t'cipher' => 'aes-128',\n\t\t\t'mode' => 'cbc',\n\t\t\t'key' => str_repeat(\"\\x0\", 16),\n\t\t\t'raw_data' => TRUE,\n\t\t\t'hmac_key' => str_repeat(\"\\x0\", 16),\n\t\t\t'hmac_digest' => 'sha256'\n\t\t);\n\n\t\t$output = $this->encryption->__get_params($params);\n\t\tunset($output['handle'], $output['cipher'], $params['raw_data'], $params['cipher']);\n\t\t$params['base64'] = FALSE;\n\t\t$this->assertEquals($params, $output);\n\n\t\t// HMAC disabled\n\t\tunset($params['hmac_key'], $params['hmac_digest']);\n\t\t$params['hmac'] = $params['raw_data'] = FALSE;\n\t\t$params['cipher'] = 'aes-128';\n\t\t$output = $this->encryption->__get_params($params);\n\t\tunset($output['handle'], $output['cipher'], $params['hmac'], $params['raw_data'], $params['cipher']);\n\t\t$params['base64'] = TRUE;\n\t\t$params['hmac_digest'] = $params['hmac_key'] = NULL;\n\t\t$this->assertEquals($params, $output);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * initialize(), encrypt(), decrypt() test\n\t *\n\t * Testing the three methods separately is not realistic as they are\n\t * designed to work together. A more thorough test for initialize()\n\t * though is the OpenSSL/MCrypt compatibility test.\n\t *\n\t * @depends\ttest_hkdf\n\t * @depends\ttest__get_params\n\t */\n\tpublic function test_initialize_encrypt_decrypt()\n\t{\n\t\t$message = 'This is a plain-text message.';\n\t\t$key = \"\\xd0\\xc9\\x08\\xc4\\xde\\x52\\x12\\x6e\\xf8\\xcc\\xdb\\x03\\xea\\xa0\\x3a\\x5c\";\n\n\t\t// Default state (AES-128/Rijndael-128 in CBC mode)\n\t\t$this->encryption->initialize(array('key' => $key));\n\n\t\t// Was the key properly set?\n\t\t$this->assertEquals($key, $this->encryption->get_key());\n\n\t\t$this->assertEquals($message, $this->encryption->decrypt($this->encryption->encrypt($message)));\n\n\t\t// Try DES3 in OFB mode, just for the sake of changing stuff\n\t\t$this->encryption->initialize(array('cipher' => 'tripledes', 'mode' => 'ofb', 'key' => substr($key, 0, 8)));\n\t\t$this->assertEquals($message, $this->encryption->decrypt($this->encryption->encrypt($message)));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * encrypt(), decrypt test with custom parameters\n\t *\n\t * @depends\ttest__get_params\n\t */\n\tpublic function test_encrypt_decrypt_custom()\n\t{\n\t\t$message = 'Another plain-text message.';\n\n\t\t// A random invalid parameter\n\t\t$this->assertFalse($this->encryption->encrypt($message, array('foo')));\n\t\t$this->assertFalse($this->encryption->decrypt($message, array('foo')));\n\n\t\t// No HMAC, binary output\n\t\t$params = array(\n\t\t\t'cipher' => 'tripledes',\n\t\t\t'mode' => 'cfb',\n\t\t\t'key' => str_repeat(\"\\x1\", 16),\n\t\t\t'base64' => FALSE,\n\t\t\t'hmac' => FALSE\n\t\t);\n\n\t\t$ciphertext = $this->encryption->encrypt($message, $params);\n\n\t\t$this->assertEquals($message, $this->encryption->decrypt($ciphertext, $params));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * _mcrypt_get_handle() test\n\t */\n\tpublic function test__mcrypt_get_handle()\n\t{\n\t\tif ($this->encryption->drivers['mcrypt'] === FALSE)\n\t\t{\n\t\t\treturn $this->markTestSkipped('Cannot test MCrypt because it is not available.');\n\t\t}\n\t\telseif (version_compare(PHP_VERSION, '7.1.0-alpha', '>='))\n\t\t{\n\t\t\treturn $this->markTestSkipped('ext/mcrypt is deprecated since PHP 7.1 and will generate notices here.');\n\t\t}\n\n\t\t$this->assertInternalType('resource', $this->encryption->__driver_get_handle('mcrypt', 'rijndael-128', 'cbc'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * _openssl_get_handle() test\n\t */\n\tpublic function test__openssl_mcrypt_get_handle()\n\t{\n\t\tif ($this->encryption->drivers['openssl'] === FALSE)\n\t\t{\n\t\t\treturn $this->markTestSkipped('Cannot test OpenSSL because it is not available.');\n\t\t}\n\n\t\t$this->assertEquals('aes-128-cbc', $this->encryption->__driver_get_handle('openssl', 'aes-128', 'cbc'));\n\t\t$this->assertEquals('rc4-40', $this->encryption->__driver_get_handle('openssl', 'rc4-40', 'stream'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * OpenSSL/MCrypt portability test\n\t *\n\t * Amongst the obvious stuff, _cipher_alias() is also tested here.\n\t */\n\tpublic function test_portability()\n\t{\n\t\tif ( ! $this->encryption->drivers['mcrypt'] OR ! $this->encryption->drivers['openssl'])\n\t\t{\n\t\t\t$this->markTestSkipped('Both MCrypt and OpenSSL support are required for portability tests.');\n\t\t\treturn;\n\t\t}\n\t\telseif (version_compare(PHP_VERSION, '7.1.0-alpha', '>='))\n\t\t{\n\t\t\treturn $this->markTestSkipped('ext/mcrypt is deprecated since PHP 7.1 and will generate notices here.');\n\t\t}\n\n\t\t$message = 'This is a message encrypted via MCrypt and decrypted via OpenSSL, or vice-versa.';\n\n\t\t// Format is: <Cipher name>, <Cipher mode>, <Key size>\n\t\t$portable = array(\n\t\t\tarray('aes-128', 'cbc', 16),\n\t\t\tarray('aes-128', 'cfb', 16),\n\t\t\tarray('aes-128', 'cfb8', 16),\n\t\t\tarray('aes-128', 'ofb', 16),\n\t\t\tarray('aes-128', 'ecb', 16),\n\t\t\tarray('aes-128', 'ctr', 16),\n\t\t\tarray('aes-192', 'cbc', 24),\n\t\t\tarray('aes-192', 'cfb', 24),\n\t\t\tarray('aes-192', 'cfb8', 24),\n\t\t\tarray('aes-192', 'ofb', 24),\n\t\t\tarray('aes-192', 'ecb', 24),\n\t\t\tarray('aes-192', 'ctr', 24),\n\t\t\tarray('aes-256', 'cbc', 32),\n\t\t\tarray('aes-256', 'cfb', 32),\n\t\t\tarray('aes-256', 'cfb8', 32),\n\t\t\tarray('aes-256', 'ofb', 32),\n\t\t\tarray('aes-256', 'ecb', 32),\n\t\t\tarray('aes-256', 'ctr', 32),\n\t\t\tarray('des', 'cbc', 7),\n\t\t\tarray('des', 'cfb', 7),\n\t\t\tarray('des', 'cfb8', 7),\n\t\t\tarray('des', 'ofb', 7),\n\t\t\tarray('des', 'ecb', 7),\n\t\t\tarray('tripledes', 'cbc', 7),\n\t\t\tarray('tripledes', 'cfb', 7),\n\t\t\tarray('tripledes', 'cfb8', 7),\n\t\t\tarray('tripledes', 'ofb', 7),\n\t\t\tarray('tripledes', 'cbc', 14),\n\t\t\tarray('tripledes', 'cfb', 14),\n\t\t\tarray('tripledes', 'cfb8', 14),\n\t\t\tarray('tripledes', 'ofb', 14),\n\t\t\tarray('tripledes', 'cbc', 21),\n\t\t\tarray('tripledes', 'cfb', 21),\n\t\t\tarray('tripledes', 'cfb8', 21),\n\t\t\tarray('tripledes', 'ofb', 21),\n\t\t\tarray('blowfish', 'cbc', 16),\n\t\t\tarray('blowfish', 'cfb', 16),\n\t\t\tarray('blowfish', 'ofb', 16),\n\t\t\tarray('blowfish', 'ecb', 16),\n\t\t\tarray('blowfish', 'cbc', 56),\n\t\t\tarray('blowfish', 'cfb', 56),\n\t\t\tarray('blowfish', 'ofb', 56),\n\t\t\tarray('blowfish', 'ecb', 56),\n\t\t\tarray('cast5', 'cbc', 11),\n\t\t\tarray('cast5', 'cfb', 11),\n\t\t\tarray('cast5', 'ofb', 11),\n\t\t\tarray('cast5', 'ecb', 11),\n\t\t\tarray('cast5', 'cbc', 16),\n\t\t\tarray('cast5', 'cfb', 16),\n\t\t\tarray('cast5', 'ofb', 16),\n\t\t\tarray('cast5', 'ecb', 16),\n\t\t\tarray('rc4', 'stream', 5),\n\t\t\tarray('rc4', 'stream', 8),\n\t\t\tarray('rc4', 'stream', 16),\n\t\t\tarray('rc4', 'stream', 32),\n\t\t\tarray('rc4', 'stream', 64),\n\t\t\tarray('rc4', 'stream', 128),\n\t\t\tarray('rc4', 'stream', 256)\n\t\t);\n\t\t$driver_index = array('mcrypt', 'openssl');\n\n\t\tforeach ($portable as &$test)\n\t\t{\n\t\t\t// Add some randomness to the selected driver\n\t\t\t$driver = mt_rand(0,1);\n\t\t\t$params = array(\n\t\t\t\t'driver' => $driver_index[$driver],\n\t\t\t\t'cipher' => $test[0],\n\t\t\t\t'mode' => $test[1],\n\t\t\t\t'key' => openssl_random_pseudo_bytes($test[2])\n\t\t\t);\n\n\t\t\t$this->encryption->initialize($params);\n\t\t\t$ciphertext = $this->encryption->encrypt($message);\n\n\t\t\t$driver = (int) ! $driver;\n\t\t\t$params['driver'] = $driver_index[$driver];\n\n\t\t\t$this->encryption->initialize($params);\n\t\t\t$this->assertEquals($message, $this->encryption->decrypt($ciphertext));\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * __get() test\n\t */\n\tpublic function test_magic_get()\n\t{\n\t\t$this->assertNull($this->encryption->foo);\n\t\t$this->assertEquals(array('mcrypt', 'openssl'), array_keys($this->encryption->drivers));\n\n\t\t// 'stream' mode is translated into an empty string for OpenSSL\n\t\t$this->encryption->initialize(array('cipher' => 'rc4', 'mode' => 'stream'));\n\t\t$this->assertEquals('stream', $this->encryption->mode);\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/libraries/Form_validation_test.php",
    "content": "<?php\n\nclass Form_validation_test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t$_SERVER['REQUEST_METHOD'] = 'POST';\n\n\t\t// Create a mock loader since load->helper() looks in the wrong directories for unit tests,\n\t\t// We'll use CI_TestCase->helper() instead\n\t\t$loader = $this->getMockBuilder('CI_Loader')->setMethods(array('helper'))->getMock();\n\n\t\t// Same applies for lang\n\t\t$lang = $this->getMockBuilder('CI_Lang')->setMethods(array('load'))->getMock();\n\n\t\t$security = new Mock_Core_Security('UTF-8');\n\t\t$input = new CI_Input($security);\n\n\t\t$this->ci_instance_var('lang', $lang);\n\t\t$this->ci_instance_var('load', $loader);\n\t\t$this->ci_instance_var('input', $input);\n\n\t\t$this->lang('form_validation');\n\t\t$this->helper('form');\n\n\t\t$this->form_validation = new CI_Form_validation();\n\t}\n\n\tpublic function test_empty_array_input()\n\t{\n\t\t$this->assertFalse(\n\t\t\t$this->run_rules(\n\t\t\t\tarray(array('field' => 'foo', 'label' => 'Foo Label', 'rules' => 'required')),\n\t\t\t\tarray('foo' => array())\n\t\t\t)\n\t\t);\n\t}\n\n\tpublic function test_rule_required()\n\t{\n\t\t$rules = array(array('field' => 'foo', 'label' => 'Foo', 'rules' => 'is_numeric'));\n\n\t\t// Empty, not required\n\t\t$this->assertTrue($this->run_rules($rules, array('foo' => '')));\n\n\t\t// Not required, but also not empty\n\t\t$this->assertTrue($this->run_rules($rules, array('foo' => '123')));\n\t\t$this->assertFalse($this->run_rules($rules, array('foo' => 'bar')));\n\n\t\t// Required variations\n\t\t$rules[0]['rules'] .= '|required';\n\t\t$this->assertTrue($this->run_rules($rules, array('foo' => '123')));\n\t\t$this->assertFalse($this->run_rules($rules, array('foo' => '')));\n\t\t$this->assertFalse($this->run_rules($rules, array('foo' => ' ')));\n\t\t$this->assertFalse($this->run_rules($rules, array('foo' => 'bar')));\n\t}\n\n\tpublic function test_rule_is_array()\n\t{\n\t\t$rules = array(array('field' => 'foo', 'label' => 'Foo', 'rules' => 'is_array'));\n\t\t$this->assertTrue($this->run_rules($rules,  array('foo' => array('1', '2'))));\n\t\t$this->assertFalse($this->run_rules($rules, array('foo' => '')));\n\t}\n\n\tpublic function test_rule_matches()\n\t{\n\t\t$rules = array(\n\t\t\tarray('field' => 'foo', 'label' => 'label', 'rules' => 'required'),\n\t\t\tarray('field' => 'bar', 'label' => 'label2', 'rules' => 'matches[foo]')\n\t\t);\n\t\t$values_base = array('foo' => 'sample');\n\n\t\t$this->assertTrue($this->run_rules($rules, array_merge($values_base, array('bar' => 'sample'))));\n\n\t\t$this->assertFalse($this->run_rules($rules, array_merge($values_base, array('bar' => ''))));\n\t\t$this->assertFalse($this->run_rules($rules, array_merge($values_base, array('bar' => 'Sample'))));\n\t\t$this->assertFalse($this->run_rules($rules, array_merge($values_base, array('bar' => ' sample'))));\n\t}\n\n\tpublic function test_rule_differs()\n\t{\n\t\t$rules = array(\n\t\t\tarray('field' => 'foo', 'label' => 'label', 'rules' => 'required'),\n\t\t\tarray('field' => 'bar', 'label' => 'label2', 'rules' => 'differs[foo]')\n\t\t);\n\t\t$values_base = array('foo' => 'sample');\n\n\t\t$this->assertTrue($this->run_rules($rules, array_merge($values_base, array('bar' => 'does_not_match'))));\n\t\t$this->assertTrue($this->run_rules($rules, array_merge($values_base, array('bar' => 'Sample'))));\n\t\t$this->assertTrue($this->run_rules($rules, array_merge($values_base, array('bar' => ' sample'))));\n\n\t\t$this->assertFalse($this->run_rules($rules, array_merge($values_base, array('bar' => 'sample'))));\n\t}\n\n\tpublic function test_rule_min_length()\n\t{\n\t\t$this->assertTrue($this->form_validation->min_length('12345', '5'));\n\t\t$this->assertTrue($this->form_validation->min_length('test', '0'));\n\n\t\t$this->assertFalse($this->form_validation->min_length('123', '4'));\n\t\t$this->assertFalse($this->form_validation->min_length('should_fail', 'A'));\n\t\t$this->assertFalse($this->form_validation->min_length('', '4'));\n\t}\n\n\tpublic function test_rule_max_length()\n\t{\n\t\t$this->assertTrue($this->form_validation->max_length('', '4'));\n\t\t$this->assertTrue($this->form_validation->max_length('1234', '4'));\n\n\t\t$this->assertFalse($this->form_validation->max_length('12345', '4'));\n\t\t$this->assertFalse($this->form_validation->max_length('should_fail', 'A'));\n\t}\n\n\tpublic function test_rule_exact_length()\n\t{\n\t\t$this->assertTrue($this->form_validation->exact_length('1234', '4'));\n\n\t\t$this->assertFalse($this->form_validation->exact_length('', '3'));\n\t\t$this->assertFalse($this->form_validation->exact_length('12345', '4'));\n\t\t$this->assertFalse($this->form_validation->exact_length('123', '4'));\n\t\t$this->assertFalse($this->form_validation->exact_length('should_fail', 'A'));\n\t}\n\n\tpublic function test_rule_greater_than()\n\t{\n\t\t$this->assertTrue($this->form_validation->greater_than('-10', '-11'));\n\t\t$this->assertTrue($this->form_validation->greater_than('10', '9'));\n\n\t\t$this->assertFalse($this->form_validation->greater_than('10', '10'));\n\t\t$this->assertFalse($this->form_validation->greater_than('10', 'a'));\n\t\t$this->assertFalse($this->form_validation->greater_than('10a', '10'));\n\t}\n\n\tpublic function test_rule_greater_than_equal_to()\n\t{\n\t\t$this->assertTrue($this->form_validation->greater_than_equal_to('0', '0'));\n\t\t$this->assertTrue($this->form_validation->greater_than_equal_to('1', '0'));\n\n\t\t$this->assertFalse($this->form_validation->greater_than_equal_to('-1', '0'));\n\t\t$this->assertFalse($this->form_validation->greater_than_equal_to('10a', '0'));\n\t}\n\n\tpublic function test_rule_less_than()\n\t{\n\t\t$this->assertTrue($this->form_validation->less_than('4', '5'));\n\t\t$this->assertTrue($this->form_validation->less_than('-1', '0'));\n\n\t\t$this->assertFalse($this->form_validation->less_than('4', '4'));\n\t\t$this->assertFalse($this->form_validation->less_than('10a', '5'));\n\t}\n\n\tpublic function test_rule_less_than_equal_to()\n\t{\n\t\t$this->assertTrue($this->form_validation->less_than_equal_to('-1', '0'));\n\t\t$this->assertTrue($this->form_validation->less_than_equal_to('-1', '-1'));\n\t\t$this->assertTrue($this->form_validation->less_than_equal_to('4', '4'));\n\n\t\t$this->assertFalse($this->form_validation->less_than_equal_to('0', '-1'));\n\t\t$this->assertFalse($this->form_validation->less_than_equal_to('10a', '0'));\n\t}\n\n\tpublic function test_rule_in_list()\n\t{\n\t\t$this->assertTrue($this->form_validation->in_list('red', 'red,Blue,123'));\n\t\t$this->assertTrue($this->form_validation->in_list('Blue', 'red,Blue,123'));\n\t\t$this->assertTrue($this->form_validation->in_list('123', 'red,Blue,123'));\n\n\t\t$this->assertFalse($this->form_validation->in_list('Red', 'red,Blue,123'));\n\t\t$this->assertFalse($this->form_validation->in_list(' red', 'red,Blue,123'));\n\t\t$this->assertFalse($this->form_validation->in_list('1234', 'red,Blue,123'));\n\t}\n\n\tpublic function test_rule_alpha()\n\t{\n\t\t$this->assertTrue($this->form_validation->alpha('abcdefghijklmnopqrstuvwxyzABCDEFGHLIJKLMNOPQRSTUVWXYZ'));\n\n\t\t$this->assertFalse($this->form_validation->alpha('abcdefghijklmnopqrstuvwxyzABCDEFGHLIJKLMNOPQRSTUVWXYZ '));\n\t\t$this->assertFalse($this->form_validation->alpha('abcdefghijklmnopqrstuvwxyzABCDEFGHLIJKLMNOPQRSTUVWXYZ1'));\n\t\t$this->assertFalse($this->form_validation->alpha('abcdefghijklmnopqrstuvwxyzABCDEFGHLIJKLMNOPQRSTUVWXYZ*'));\n\t}\n\n\tpublic function test_rule_alpha_numeric()\n\t{\n\t\t$this->assertTrue($this->form_validation->alpha_numeric('abcdefghijklmnopqrstuvwxyzABCDEFGHLIJKLMNOPQRSTUVWXYZ0123456789'));\n\n\t\t$this->assertFalse($this->form_validation->alpha_numeric('abcdefghijklmnopqrstuvwxyzABCDEFGHLIJKLMNOPQRSTUVWXYZ0123456789\\ '));\n\t\t$this->assertFalse($this->form_validation->alpha_numeric('abcdefghijklmnopqrstuvwxyzABCDEFGHLIJKLMNOPQRSTUVWXYZ0123456789_'));\n\t}\n\n\tpublic function test_rule_alpha_numeric_spaces()\n\t{\n\t\t$this->assertTrue($this->form_validation->alpha_numeric_spaces(' abcdefghijklmnopqrstuvwxyzABCDEFGHLIJKLMNOPQRSTUVWXYZ0123456789'));\n\n\t\t$this->assertFalse($this->form_validation->alpha_numeric_spaces(' abcdefghijklmnopqrstuvwxyzABCDEFGHLIJKLMNOPQRSTUVWXYZ0123456789_'));\n\t}\n\n\tpublic function test_rule_alpha_dash()\n\t{\n\t\t$this->assertTrue($this->form_validation->alpha_dash('abcdefghijklmnopqrstuvwxyzABCDEFGHLIJKLMNOPQRSTUVWXYZ0123456789-_'));\n\n\t\t$this->assertFalse($this->form_validation->alpha_dash('abcdefghijklmnopqrstuvwxyzABCDEFGHLIJKLMNOPQRSTUVWXYZ0123456789-_\\ '));\n\t}\n\n\tpublic function test_rule_numeric()\n\t{\n\t\t$this->assertTrue($this->form_validation->numeric('0'));\n\t\t$this->assertTrue($this->form_validation->numeric('12314'));\n\t\t$this->assertTrue($this->form_validation->numeric('-42'));\n\n\t\t$this->assertFalse($this->form_validation->numeric('123a'));\n\t\t$this->assertFalse($this->form_validation->numeric('--1'));\n\t}\n\n\tpublic function test_rule_integer()\n\t{\n\t\t$this->assertTrue($this->form_validation->integer('0'));\n\t\t$this->assertTrue($this->form_validation->integer('42'));\n\t\t$this->assertTrue($this->form_validation->integer('-1'));\n\n\t\t$this->assertFalse($this->form_validation->integer('124a'));\n\t\t$this->assertFalse($this->form_validation->integer('1.9'));\n\t\t$this->assertFalse($this->form_validation->integer('--1'));\n\t}\n\n\tpublic function test_rule_decimal()\n\t{\n\t\t$this->assertTrue($this->form_validation->decimal('1.0'));\n\t\t$this->assertTrue($this->form_validation->decimal('-0.98'));\n\n\t\t$this->assertFalse($this->form_validation->decimal('0'));\n\t\t$this->assertFalse($this->form_validation->decimal('1.0a'));\n\t\t$this->assertFalse($this->form_validation->decimal('-i'));\n\t\t$this->assertFalse($this->form_validation->decimal('--1'));\n\t}\n\n\tpublic function test_rule_is_natural()\n\t{\n\t\t$this->assertTrue($this->form_validation->is_natural('0'));\n\t\t$this->assertTrue($this->form_validation->is_natural('12'));\n\n\t\t$this->assertFalse($this->form_validation->is_natural('42a'));\n\t\t$this->assertFalse($this->form_validation->is_natural('-1'));\n\t}\n\n\tpublic function test_rule_is_natural_no_zero()\n\t{\n\t\t$this->assertTrue($this->form_validation->is_natural_no_zero('42'));\n\n\t\t$this->assertFalse($this->form_validation->is_natural_no_zero('0'));\n\t\t$this->assertFalse($this->form_validation->is_natural_no_zero('42a'));\n\t\t$this->assertFalse($this->form_validation->is_natural_no_zero('-1'));\n\t}\n\n\tpublic function test_rule_valid_url()\n\t{\n\t\t$this->assertTrue($this->form_validation->valid_url('www.codeigniter.com'));\n\t\t$this->assertTrue($this->form_validation->valid_url('http://codeigniter.com'));\n\n\t\t// https://bugs.php.net/bug.php?id=51192\n\t\t$this->assertTrue($this->form_validation->valid_url('http://accept-dashes.tld'));\n\t\t$this->assertFalse($this->form_validation->valid_url('http://reject_underscores.tld'));\n\n\t\t// https://github.com/bcit-ci/CodeIgniter/issues/4415\n\t\t$this->assertTrue($this->form_validation->valid_url('http://[::1]/ipv6'));\n\n\t\t// URI scheme case-sensitivity: https://github.com/bcit-ci/CodeIgniter/pull/4758\n\t\t$this->assertTrue($this->form_validation->valid_url('HtTp://127.0.0.1/'));\n\n\t\t// https://github.com/bcit-ci/CodeIgniter/issues/5755\n\t\t$this->assertFalse($this->form_validation->valid_url('1'));\n\n\t\t$this->assertFalse($this->form_validation->valid_url('htt://www.codeIgniter.com'));\n\t\t$this->assertFalse($this->form_validation->valid_url(''));\n\t\t$this->assertFalse($this->form_validation->valid_url('code igniter'));\n\t}\n\n\tpublic function test_rule_valid_email()\n\t{\n\t\t$this->assertTrue($this->form_validation->valid_email('email@sample.com'));\n\t\t$this->assertFalse($this->form_validation->valid_email('email@sample.com foo bar'));\n\t\t$this->assertFalse($this->form_validation->valid_email('valid_email', '@sample.com'));\n\t}\n\n\tpublic function test_rule_valid_emails()\n\t{\n\t\t$this->assertTrue($this->form_validation->valid_emails('1@sample.com,2@sample.com'));\n\t\t$this->assertTrue($this->form_validation->valid_emails('email@sample.com'));\n\n\t\t$this->assertFalse($this->form_validation->valid_emails('valid_email', '@sample.com'));\n\t\t$this->assertFalse($this->form_validation->valid_emails('@sample.com,2@sample.com,validemail@email.ca'));\n\t}\n\n\tpublic function test_rule_valid_ip()\n\t{\n\t\t$this->assertTrue($this->form_validation->valid_ip('127.0.0.1'));\n\t\t$this->assertTrue($this->form_validation->valid_ip('127.0.0.1', 'ipv4'));\n\t\t$this->assertTrue($this->form_validation->valid_ip('2001:0db8:85a3:0000:0000:8a2e:0370:7334'));\n\t\t$this->assertTrue($this->form_validation->valid_ip('2001:0db8:85a3:0000:0000:8a2e:0370:7334', 'ipv6'));\n\n\t\t$this->assertFalse($this->form_validation->valid_ip('2001:0db8:85a3:0000:0000:8a2e:0370:7334', 'ipv4'));\n\t\t$this->assertFalse($this->form_validation->valid_ip('127.0.0.1', 'ipv6'));\n\t\t$this->assertFalse($this->form_validation->valid_ip('H001:0db8:85a3:0000:0000:8a2e:0370:7334'));\n\t\t$this->assertFalse($this->form_validation->valid_ip('127.0.0.259'));\n\t}\n\n\tpublic function test_rule_valid_mac()\n\t{\n\t\t$this->assertTrue($this->form_validation->valid_mac(\"01-23-45-67-89-aB\"));\n\t\t$this->assertTrue($this->form_validation->valid_mac(\"01:23:45:67:89:aB\"));\n\t\t$this->assertTrue($this->form_validation->valid_mac(\"0123.4567.89aB\"));\n\n\t\t$this->assertFalse($this->form_validation->valid_mac(\"-01-23-45-67-89-ab\"));\n\t\t$this->assertFalse($this->form_validation->valid_mac(\"01:23:45:67:89:ab:\"));\n\t\t$this->assertFalse($this->form_validation->valid_mac(\"01:23:45:67:89:ab\\n\"));\n\t\t$this->assertFalse($this->form_validation->valid_mac(\"01:23:45:67:89:ag:\"));\n\t\t$this->assertFalse($this->form_validation->valid_mac('0123456789ab'));\n\t}\n\n\tpublic function test_rule_valid_base64()\n\t{\n\t\t$this->assertTrue($this->form_validation->valid_base64(base64_encode('string')));\n\t\t$this->assertFalse($this->form_validation->valid_base64('FA08GG'));\n\t}\n\n\tpublic function test_set_data()\n\t{\n\t\t$data = array('field' => 'some_data');\n\t\t$this->form_validation->set_data($data);\n\t\t$this->form_validation->set_rules('field', 'label', 'required');\n\t\t$this->assertTrue($this->form_validation->run());\n\n\t\t// Test with empty array\n\t\t$_POST = array();\n\t\t$this->form_validation->reset_validation();\n\t\t$data = array('field' => 'some_data');\n\t\t$this->form_validation->set_data($data);\n\t\t// This should do nothing. Old data will still be used\n\t\t$this->form_validation->set_data(array());\n\t\t$this->form_validation->set_rules('field', 'label', 'required');\n\t\t$this->assertTrue($this->form_validation->run());\n\t}\n\n\tpublic function test_set_message()\n\t{\n\t\t$err_message = 'What a terrible error!';\n\t\t$rules = array(\n\t\t\tarray(\n\t\t\t\t'field' => 'req_field',\n\t\t\t\t'label' => 'label',\n\t\t\t\t'rules' => 'required'\n\t\t\t)\n\t\t);\n\t\t$errorless_data = array('req_field' => 'some text');\n\t\t$erroneous_data = array('req_field' => '');\n\n\t\t$this->form_validation->set_message('required', $err_message);\n\t\t$this->form_validation->set_data($erroneous_data);\n\t\t$this->form_validation->set_rules($rules);\n\t\t$this->form_validation->run();\n\t\t$this->assertEquals('<p>'.$err_message.'</p>', $this->form_validation->error('req_field'));\n\n\t\t$this->form_validation->reset_validation();\n\t\t$this->form_validation->set_message('required', $err_message);\n\t\t$this->form_validation->set_data($errorless_data);\n\t\t$this->form_validation->set_rules($rules);\n\t\t$this->form_validation->run();\n\t\t$this->assertEquals('', $this->form_validation->error('req_field'));\n\t}\n\n\tpublic function test_set_error_delimiters()\n\t{\n\t\t$prefix = '<div class=\"error\">';\n\t\t$suffix = '</div>';\n\t\t$this->form_validation->set_error_delimiters($prefix, $suffix);\n\t\t$this->form_validation->set_rules('foo', 'label', 'required');\n\t\t$_POST = array('foo' => '');\n\t\t$this->form_validation->run();\n\t\t$error_msg = $this->form_validation->error('foo');\n\n\t\t$this->assertStringStartsWith($prefix, $error_msg);\n\t\t$this->assertTrue(strrpos($error_msg, $suffix, -strlen($suffix)) === (strlen($error_msg) - strlen($suffix)));\n\n\t\t$_POST = array();\n\t}\n\n\tpublic function test_error_array()\n\t{\n\t\t$error_message = 'What a terrible error!';\n\t\t$this->form_validation->set_message('required', $error_message);\n\t\t$this->form_validation->set_rules('foo', 'label', 'required');\n\t\t$_POST = array('foo' => '');\n\t\t$this->form_validation->run();\n\t\t$error_array = $this->form_validation->error_array();\n\t\t$this->assertEquals($error_message, $error_array['foo']);\n\n\t\t$_POST = array();\n\t}\n\n\tpublic function test_error_string()\n\t{\n\t\t$error_message = 'What a terrible error!';\n\t\t$prefix_default = '<foo>';\n\t\t$suffix_default = '</foo>';\n\t\t$prefix_test = '<bar>';\n\t\t$suffix_test = '</bar>';\n\t\t$this->form_validation->set_error_delimiters($prefix_default, $suffix_default);\n\t\t$this->form_validation->set_message('required', $error_message);\n\t\t$this->form_validation->set_rules('foo', 'label', 'required');\n\t\t$_POST = array('foo' => '');\n\t\t$this->form_validation->run();\n\n\t\t$this->assertEquals($prefix_default.$error_message.$suffix_default.\"\\n\", $this->form_validation->error_string());\n\t\t$this->assertEquals($prefix_test.$error_message.$suffix_default.\"\\n\", $this->form_validation->error_string($prefix_test, ''));\n\t\t$this->assertEquals($prefix_default.$error_message.$suffix_test.\"\\n\", $this->form_validation->error_string('', $suffix_test));\n\t\t$this->assertEquals($prefix_test.$error_message.$suffix_test.\"\\n\", $this->form_validation->error_string($prefix_test, $suffix_test));\n\n\t\t$this->form_validation->reset_validation();\n\t\t$this->form_validation->set_rules('foo', 'label', 'required');\n\t\t$_POST = array('foo' => 'bar');\n\t\t$this->form_validation->run();\n\t\t$this->assertEquals('', $this->form_validation->error_string());\n\n\t\t$_POST = array();\n\t}\n\n\tpublic function test_run()\n\t{\n\t\t// form_validation->run() is tested in many of the other unit tests\n\t\t// This test will only test run(group='') when group is not empty\n\t\t$config = array(\n\t\t\t'pass' => array(\n\t\t\t\tarray(\n\t\t\t\t\t'field' => 'username',\n\t\t\t\t\t'label' => 'user',\n\t\t\t\t\t'rules' => 'alpha_numeric'\n\t\t\t\t)\n\t\t\t),\n\t\t\t'fail' => array(\n\t\t\t\tarray(\n\t\t\t\t\t'field' => 'username',\n\t\t\t\t\t'label' => 'user',\n\t\t\t\t\t'rules' => 'alpha'\n\t\t\t\t)\n\t\t\t)\n\t\t);\n\t\t$_POST = array('username' => 'foo42');\n\t\t$form_validation = new CI_Form_validation($config);\n\t\t$this->assertTrue($form_validation->run('pass'));\n\n\t\t$form_validation = new CI_Form_validation($config);\n\t\t$this->assertFalse($form_validation->run('fail'));\n\n\t\t$_POST = array();\n\t}\n\n\tpublic function test_set_rules_exception()\n\t{\n\t\t$this->setExpectedException('BadMethodCallException');\n\t\t$this->form_validation->set_rules('foo', 'bar');\n\t}\n\n\tpublic function test_has_rule()\n\t{\n\t\t$this->form_validation->set_rules('foo', 'label', 'required');\n\n\t\t$this->assertTrue($this->form_validation->has_rule('foo'));\n\t\t$this->assertFalse($this->form_validation->has_rule('bar'));\n\t}\n\n\tpublic function test_set_value()\n\t{\n\t\t$default = 'default';\n\t\t$this->form_validation->set_rules('foo', 'label', 'required');\n\t\t$this->form_validation->set_rules('bar[]', 'label', 'required');\n\n\t\t// No post data yet: should return the default value provided\n\t\t$this->assertEquals($default, $this->form_validation->set_value('foo', $default));\n\t\t$_POST = array('foo' => 'foo', 'bar' => array('bar1', 'bar2'));\n\t\t$this->form_validation->run();\n\t\t$this->assertEquals('foo', $this->form_validation->set_value('foo', $default));\n\t\t$this->assertEquals('bar1', $this->form_validation->set_value('bar[]', $default));\n\t\t$this->assertEquals('bar2', $this->form_validation->set_value('bar[]', $default));\n\n\t\t$_POST = array();\n\t}\n\n\tpublic function test_issue_5202()\n\t{\n\t\t$data = array('person' => array('firstname' => 'Dick', 'lastname' => 'Tracy '));\n\t\t$this->form_validation->set_rules('person[firstname]', 'First Name', 'required|trim');\n\t\t$this->form_validation->set_rules('person[lastname]', 'Last Name', 'required|trim');\n\t\t$this->form_validation->set_data($data);\n\t\t$valid = $this->form_validation->run('', $data);\n\n\t\t$this->assertTrue($valid);\n\t\t$this->assertEquals('Dick', $data['person']['firstname']);\n\t\t$this->assertEquals('Tracy', $data['person']['lastname']);\n\t}\n\n\tpublic function test_set_select()\n\t{\n\t\t// Test 1: No options selected\n\t\t$this->form_validation->run();\n\n\t\t$this->assertEquals('', $this->form_validation->set_select('select', 'foo'));\n\t\t$this->assertEquals(' selected=\"selected\"', $this->form_validation->set_select('select', 'bar', TRUE));\n\n\t\t// Test 2: 1 option selected\n\t\t$this->form_validation->reset_validation();\n\t\t$this->form_validation->set_rules('select', 'label', 'alpha_numeric');\n\t\t$_POST = array('select' => 'foo');\n\t\t$this->form_validation->run();\n\n\t\t$this->assertEquals(' selected=\"selected\"', $this->form_validation->set_select('select', 'foo'));\n\t\t$this->assertEquals(' selected=\"selected\"', $this->form_validation->set_select('select', 'foo', TRUE));\n\t\t$this->assertEquals('', $this->form_validation->set_select('select', 'bar'));\n\t\t$this->assertEquals('', $this->form_validation->set_select('select', 'bar', TRUE));\n\n\t\t// Test 3: Multiple options selected\n\t\t$this->form_validation->reset_validation();\n\t\t$this->form_validation->set_rules('select[]', 'label', 'alpha_numeric');\n\t\t$_POST = array('select' => array('foo', 'bar'));\n\t\t$this->form_validation->run();\n\n\t\t$this->assertEquals(' selected=\"selected\"', $this->form_validation->set_select('select[]', 'foo'));\n\t\t$this->assertEquals(' selected=\"selected\"', $this->form_validation->set_select('select[]', 'foo', TRUE));\n\t\t$this->assertEquals(' selected=\"selected\"', $this->form_validation->set_select('select[]', 'bar'));\n\t\t$this->assertEquals(' selected=\"selected\"', $this->form_validation->set_select('select[]', 'bar', TRUE));\n\t\t$this->assertEquals('', $this->form_validation->set_select('select[]', 'foobar'));\n\t\t$this->assertEquals('', $this->form_validation->set_select('select[]', 'foobar', TRUE));\n\n\t\t$_POST = array();\n\t}\n\n\tpublic function test_set_radio()\n\t{\n\t\t// Test 1: No options selected\n\t\t$this->form_validation->run();\n\n\t\t$this->assertEquals('', $this->form_validation->set_radio('select', 'foo'));\n\t\t// Default should only work when no rules are set\n\t\t$this->assertEquals(' checked=\"checked\"', $this->form_validation->set_radio('select', 'bar', TRUE));\n\n\t\t// Test 2: 1 option selected\n\t\t$this->form_validation->reset_validation();\n\t\t$this->form_validation->set_rules('select', 'label', 'alpha_numeric');\n\t\t$_POST = array('select' => 'foo');\n\t\t$this->form_validation->run();\n\n\t\t$this->assertEquals(' checked=\"checked\"', $this->form_validation->set_radio('select', 'foo'));\n\t\t$this->assertEquals(' checked=\"checked\"', $this->form_validation->set_radio('select', 'foo', TRUE));\n\t\t$this->assertEquals('', $this->form_validation->set_radio('select', 'bar'));\n\t\t$this->assertEquals('', $this->form_validation->set_radio('select', 'bar', TRUE));\n\n\t\t// Test 3: Multiple options checked\n\t\t$this->form_validation->reset_validation();\n\t\t$this->form_validation->set_rules('select[]', 'label', 'alpha_numeric');\n\t\t$_POST = array('select' => array('foo', 'bar'));\n\t\t$this->form_validation->run();\n\n\t\t$this->assertEquals(' checked=\"checked\"', $this->form_validation->set_radio('select[]', 'foo'));\n\t\t$this->assertEquals(' checked=\"checked\"', $this->form_validation->set_radio('select[]', 'foo', TRUE));\n\t\t$this->assertEquals(' checked=\"checked\"', $this->form_validation->set_radio('select[]', 'bar'));\n\t\t$this->assertEquals(' checked=\"checked\"', $this->form_validation->set_radio('select[]', 'bar', TRUE));\n\t\t$this->assertEquals('', $this->form_validation->set_radio('select[]', 'foobar'));\n\t\t$this->assertEquals('', $this->form_validation->set_radio('select[]', 'foobar', TRUE));\n\n\t\t$_POST = array();\n\t}\n\n\tpublic function test_set_checkbox()\n\t{\n\t\t// Test 1: No options selected\n\t\t$this->form_validation->run();\n\n\t\t$this->assertEquals('', $this->form_validation->set_checkbox('select', 'foo'));\n\t\t$this->assertEquals(' checked=\"checked\"', $this->form_validation->set_checkbox('select', 'bar', TRUE));\n\n\t\t// Test 2: 1 option selected\n\t\t$this->form_validation->reset_validation();\n\t\t$this->form_validation->set_rules('select', 'label', 'alpha_numeric');\n\t\t$_POST = array('select' => 'foo');\n\t\t$this->form_validation->run();\n\n\t\t$this->assertEquals(' checked=\"checked\"', $this->form_validation->set_checkbox('select', 'foo'));\n\t\t$this->assertEquals(' checked=\"checked\"', $this->form_validation->set_checkbox('select', 'foo', TRUE));\n\t\t$this->assertEquals('', $this->form_validation->set_checkbox('select', 'bar'));\n\t\t$this->assertEquals('', $this->form_validation->set_checkbox('select', 'bar', TRUE));\n\n\t\t// Test 3: Multiple options selected\n\t\t$this->form_validation->reset_validation();\n\t\t$this->form_validation->set_rules('select[]', 'label', 'alpha_numeric');\n\t\t$_POST = array('select' => array('foo', 'bar'));\n\t\t$this->form_validation->run();\n\n\t\t$this->assertEquals(' checked=\"checked\"', $this->form_validation->set_checkbox('select[]', 'foo'));\n\t\t$this->assertEquals(' checked=\"checked\"', $this->form_validation->set_checkbox('select[]', 'foo', TRUE));\n\t\t$this->assertEquals(' checked=\"checked\"', $this->form_validation->set_checkbox('select[]', 'bar'));\n\t\t$this->assertEquals(' checked=\"checked\"', $this->form_validation->set_checkbox('select[]', 'bar', TRUE));\n\t\t$this->assertEquals('', $this->form_validation->set_checkbox('select[]', 'foobar'));\n\t\t$this->assertEquals('', $this->form_validation->set_checkbox('select[]', 'foobar', TRUE));\n\n\t\t$_POST = array();\n\t}\n\n\tpublic function test_regex_match()\n\t{\n\t\t$regex = '/f[a-zA-Z]+/';\n\t\t$this->assertTrue($this->form_validation->regex_match('foo', $regex));\n\t\t$this->assertFalse($this->form_validation->regex_match('bar', $regex));\n\t}\n\n\tpublic function test_prep_url()\n\t{\n\t\t$this->assertEquals('', $this->form_validation->prep_url(''));\n\t\t$this->assertEquals('http://codeigniter.com', $this->form_validation->prep_url('codeigniter.com'));\n\t\t$this->assertEquals('https://codeigniter.com', $this->form_validation->prep_url('https://codeigniter.com'));\n\t\t$this->assertEquals('http://codeigniter.com', $this->form_validation->prep_url('http://codeigniter.com'));\n\t\t$this->assertEquals('http://www.codeigniter.com', $this->form_validation->prep_url('www.codeigniter.com'));\n\t}\n\n\tpublic function test_encode_php_tags()\n\t{\n\t\t$this->assertEquals(\"&lt;?php\", $this->form_validation->encode_php_tags('<?php'));\n\t\t$this->assertEquals('?&gt;', $this->form_validation->encode_php_tags('?>'));\n\t}\n\n\tpublic function test_validated_data_assignment()\n\t{\n\t\t$_POST = $post_original = array('foo' => ' bar ', 'bar' => 'baz');\n\n\t\t$this->form_validation->set_data($_POST);\n\t\t$this->form_validation->set_rules('foo', 'Foo', 'required|trim');\n\n\t\t$data_processed = NULL;\n\t\t$validation_result = $this->form_validation->run('', $data_processed);\n\n\t\t$this->assertTrue($validation_result);\n\t\t$this->assertEquals($post_original, $_POST);\n\t\t$this->assertEquals(array('foo' => 'bar', 'bar' => 'baz'), $data_processed);\n\n\t\t$_POST = array();\n\t}\n\n\t/**\n\t * Run rules\n\t *\n\t * Helper method to set rules and run them at once, not\n\t * an actual test case.\n\t */\n\tpublic function run_rules($rules, $values)\n\t{\n\t\t$this->form_validation->reset_validation();\n\t\t$_POST = array();\n\t\t$this->form_validation->set_rules($rules);\n\n\t\tforeach ($values as $field => $value)\n\t\t{\n\t\t\t$_POST[$field] = $value;\n\t\t}\n\n\t\t$valid = $this->form_validation->run();\n\t\t$_POST = array();\n\n\t\treturn $valid;\n\t}\n}\n"
  },
  {
    "path": "tests/codeigniter/libraries/Parser_test.php",
    "content": "<?php\n\nclass Parser_test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t$this->parser = new CI_Parser();\n\t\t$this->ci_instance_var('parser', $this->parser);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_set_delimiters()\n\t{\n\t\t// Make sure default delimiters are there\n\t\t$this->assertEquals('{', $this->parser->l_delim);\n\t\t$this->assertEquals('}', $this->parser->r_delim);\n\n\t\t// Change them to square brackets\n\t\t$this->parser->set_delimiters('[', ']');\n\n\t\t// Make sure they changed\n\t\t$this->assertEquals('[', $this->parser->l_delim);\n\t\t$this->assertEquals(']', $this->parser->r_delim);\n\n\t\t// Reset them\n\t\t$this->parser->set_delimiters();\n\n\t\t// Make sure default delimiters are there\n\t\t$this->assertEquals('{', $this->parser->l_delim);\n\t\t$this->assertEquals('}', $this->parser->r_delim);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_parse_string()\n\t{\n\t\t$data = array(\n\t\t\t'title' => 'Page Title',\n\t\t\t'body' => 'Lorem ipsum dolor sit amet.'\n\t\t);\n\n\t\t$template = \"{title}\\n{body}\";\n\n\t\t$result = implode(\"\\n\", $data);\n\n\t\t$this->assertEquals($result, $this->parser->parse_string($template, $data, TRUE));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_parse()\n\t{\n\t\t$this->_parse_no_template();\n\t\t$this->_parse_var_pair();\n\t\t$this->_mismatched_var_pair();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tprivate function _parse_no_template()\n\t{\n\t\t$this->assertFalse($this->parser->parse_string('', '', TRUE));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tprivate function _parse_var_pair()\n\t{\n\t\t$data = array(\n\t\t\t'title'\t\t=> 'Super Heroes',\n\t\t\t'powers'\t=> array(array('invisibility' => 'yes', 'flying' => 'no'))\n\t\t);\n\n\t\t$template = \"{title}\\n{powers}{invisibility}\\n{flying}{/powers}\\nsecond:{powers} {invisibility} {flying}{/powers}\";\n\n\t\t$this->assertEquals(\"Super Heroes\\nyes\\nno\\nsecond: yes no\", $this->parser->parse_string($template, $data, TRUE));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tprivate function _mismatched_var_pair()\n\t{\n\t\t$data = array(\n\t\t\t'title'\t\t=> 'Super Heroes',\n\t\t\t'powers'\t=> array(array('invisibility' => 'yes', 'flying' => 'no'))\n\t\t);\n\n\t\t$template = \"{title}\\n{powers}{invisibility}\\n{flying}\";\n\t\t$result = \"Super Heroes\\n{powers}{invisibility}\\n{flying}\";\n\n\t\t$this->assertEquals($result, $this->parser->parse_string($template, $data, TRUE));\n\t}\n\n}"
  },
  {
    "path": "tests/codeigniter/libraries/Session_test.php",
    "content": "<?php\n\n/**\n * Session driver library unit test\n */\nclass Session_test extends CI_TestCase {\n\n\tprotected $settings = array(\n\t\t'use_cookies' => 0,\n\t\t'use_only_cookies' => 0,\n\t\t'cache_limiter' => FALSE\n\t);\n\tprotected $setting_vals = array();\n\tprotected $cookie_vals;\n\tprotected $session;\n\n\t/**\n\t * Set up test framework\n\t */\n\tpublic function set_up()\n\t{\nreturn;\n\t\t// Override settings\n\t\tforeach ($this->settings as $name => $value) {\n\t\t\t$this->setting_vals[$name] = ini_get('session.'.$name);\n\t\t\tini_set('session.'.$name, $value);\n\t\t}\n\n\t\t// Start with clean environment\n\t\t$this->cookie_vals = $_COOKIE;\n\t\t$_COOKIE = array();\n\n\t\t// Set subclass prefix to match our mock\n\t\t$this->ci_set_config('subclass_prefix', 'Mock_Libraries_');\n\n\t\t// Establish necessary support classes\n\t\t$ci = $this->ci_instance();\n\t\t$ldr = $this->ci_core_class('load');\n\t\t$ci->load = new $ldr();\n\t\t$security = new Mock_Core_Security('UTF-8');\n\t\t$ci->input = new CI_Input($security);\n\n\t\t// Make sure string helper is available\n\t\t$this->ci_vfs_clone('system/helpers/string_helper.php');\n\n\t\t// Attach session instance locally\n\t\t$config = array(\n\t\t\t'sess_encrypt_cookie' => FALSE,\n\t\t\t'sess_use_database' => FALSE,\n\t\t\t'sess_table_name' => '',\n\t\t\t'sess_expiration' => 7200,\n\t\t\t'sess_expire_on_close' => FALSE,\n\t\t\t'sess_match_ip' => FALSE,\n\t\t\t'sess_match_useragent' => TRUE,\n\t\t\t'sess_cookie_name' => 'ci_session',\n\t\t\t'cookie_path' => '',\n\t\t\t'cookie_domain' => '',\n\t\t\t'cookie_secure' => FALSE,\n\t\t\t'cookie_httponly' => FALSE,\n\t\t\t'sess_time_to_update' => 300,\n\t\t\t'time_reference' => 'local',\n\t\t\t'cookie_prefix' => '',\n\t\t\t'encryption_key' => 'foobar'\n\t\t);\n\t\t$this->session = new Mock_Libraries_Session($config);\n\t}\n\n\t/**\n\t * Tear down test framework\n\t */\n\tpublic function tear_down()\n\t{\nreturn;\n\t\t// Restore environment\n\t\tif (session_id()) session_destroy();\n\t\t$_SESSION = array();\n\t\t$_COOKIE = $this->cookie_vals;\n\n\t\t// Restore settings\n\t\tforeach ($this->settings as $name => $value) {\n\t\t\tini_set('session.'.$name, $this->setting_vals[$name]);\n\t\t}\n\t}\n\n\t/**\n\t * Test set_userdata() function\n\t */\n\tpublic function test_set_userdata()\n\t{\nreturn;\n\t\t// Set userdata values for each driver\n\t\t$key1 = 'test1';\n\t\t$ckey2 = 'test2';\n\t\t$nkey2 = 'test3';\n\t\t$cmsg1 = 'Some test data';\n\t\t$cmsg2 = 42;\n\t\t$nmsg1 = 'Other test data';\n\t\t$nmsg2 = TRUE;\n\t\t$this->session->cookie->set_userdata($key1, $cmsg1);\n\t\t$this->session->set_userdata($ckey2, $cmsg2);\n\t\t$this->session->native->set_userdata($key1, $nmsg1);\n\t\t$this->session->set_userdata($nkey2, $nmsg2);\n\n\t\t// Verify independent messages\n\t\t$this->assertEquals($cmsg1, $this->session->cookie->userdata($key1));\n\t\t$this->assertEquals($nmsg1, $this->session->native->userdata($key1));\n\n\t\t// Verify pre-selected driver sets\n\t\t$this->assertEquals($cmsg2, $this->session->cookie->userdata($ckey2));\n\t\t$this->assertEquals($nmsg2, $this->session->native->userdata($nkey2));\n\n\t\t// Verify no crossover\n\t\t$this->assertNull($this->session->cookie->userdata($nkey2));\n\t\t$this->assertNull($this->session->native->userdata($ckey2));\n\t}\n\n\t/**\n\t * Test the has_userdata() function\n\t */\n\tpublic function test_has_userdata()\n\t{\nreturn;\n\t\t// Set a userdata value for each driver\n\t\t$key = 'hastest';\n\t\t$cmsg = 'My test data';\n\t\t$this->session->cookie->set_userdata($key, $cmsg);\n\t\t$nmsg = 'Your test data';\n\t\t$this->session->native->set_userdata($key, $nmsg);\n\n\t\t// Verify values exist\n\t\t$this->assertTrue($this->session->cookie->has_userdata($key));\n\t\t$this->assertTrue($this->session->native->has_userdata($key));\n\n\t\t// Verify non-existent values\n\t\t$nokey = 'hasnot';\n\t\t$this->assertFalse($this->session->cookie->has_userdata($nokey));\n\t\t$this->assertFalse($this->session->native->has_userdata($nokey));\n\t}\n\n\t/**\n\t * Test the all_userdata() function\n\t */\n\tpublic function test_all_userdata()\n\t{\nreturn;\n\t\t// Set a specific series of data for each driver\n\t\t$cdata = array(\n\t\t\t'one' => 'first',\n\t\t\t'two' => 'second',\n\t\t\t'three' => 'third',\n\t\t\t'foo' => 'bar',\n\t\t\t'bar' => 'baz'\n\t\t);\n\t\t$ndata = array(\n\t\t\t'one' => 'gold',\n\t\t\t'two' => 'silver',\n\t\t\t'three' => 'bronze',\n\t\t\t'foo' => 'baz',\n\t\t\t'bar' => 'foo'\n\t\t);\n\t\t$this->session->cookie->set_userdata($cdata);\n\t\t$this->session->native->set_userdata($ndata);\n\n\t\t// Make sure all values are present\n\t\t$call = $this->session->cookie->userdata();\n\t\tforeach ($cdata as $key => $value) {\n\t\t\t$this->assertEquals($value, $call[$key]);\n\t\t}\n\t\t$nall = $this->session->native->userdata();\n\t\tforeach ($ndata as $key => $value) {\n\t\t\t$this->assertEquals($value, $nall[$key]);\n\t\t}\n\t}\n\n\t/**\n\t * Test the unset_userdata() function\n\t */\n\tpublic function test_unset_userdata()\n\t{\nreturn;\n\t\t// Set a userdata message for each driver\n\t\t$key = 'untest';\n\t\t$cmsg = 'Other test data';\n\t\t$this->session->cookie->set_userdata($key, $cmsg);\n\t\t$nmsg = 'Sundry test data';\n\t\t$this->session->native->set_userdata($key, $nmsg);\n\n\t\t// Verify independent messages\n\t\t$this->assertEquals($this->session->cookie->userdata($key), $cmsg);\n\t\t$this->assertEquals($this->session->native->userdata($key), $nmsg);\n\n\t\t// Unset them and verify absence\n\t\t$this->session->cookie->unset_userdata($key);\n\t\t$this->session->native->unset_userdata($key);\n\t\t$this->assertNull($this->session->cookie->userdata($key));\n\t\t$this->assertNull($this->session->native->userdata($key));\n\t}\n\n\t/**\n\t * Test the flashdata() functions\n\t */\n\tpublic function test_flashdata()\n\t{\nreturn;\n\t\t// Set flashdata message for each driver\n\t\t$key = 'fltest';\n\t\t$cmsg = 'Some flash data';\n\t\t$this->session->cookie->set_flashdata($key, $cmsg);\n\t\t$nmsg = 'Other flash data';\n\t\t$this->session->native->set_flashdata($key, $nmsg);\n\n\t\t// Simulate page reload\n\t\t$this->session->cookie->reload();\n\t\t$this->session->native->reload();\n\n\t\t// Verify independent messages\n\t\t$this->assertEquals($cmsg, $this->session->cookie->flashdata($key));\n\t\t$this->assertEquals($nmsg, $this->session->native->flashdata($key));\n\n\t\t// Simulate next page reload\n\t\t$this->session->cookie->reload();\n\t\t$this->session->native->reload();\n\n\t\t// Verify absence of messages\n\t\t$this->assertNull($this->session->cookie->flashdata($key));\n\t\t$this->assertNull($this->session->native->flashdata($key));\n\t}\n\n\t/**\n\t * Test the keep_flashdata() function\n\t */\n\tpublic function test_keep_flashdata()\n\t{\nreturn;\n\t\t// Set flashdata message for each driver\n\t\t$key = 'kfltest';\n\t\t$cmsg = 'My flash data';\n\t\t$this->session->cookie->set_flashdata($key, $cmsg);\n\t\t$nmsg = 'Your flash data';\n\t\t$this->session->native->set_flashdata($key, $nmsg);\n\n\t\t// Simulate page reload and verify independent messages\n\t\t$this->session->cookie->reload();\n\t\t$this->session->native->reload();\n\t\t$this->assertEquals($cmsg, $this->session->cookie->flashdata($key));\n\t\t$this->assertEquals($nmsg, $this->session->native->flashdata($key));\n\n\t\t// Keep messages\n\t\t$this->session->cookie->keep_flashdata($key);\n\t\t$this->session->native->keep_flashdata($key);\n\n\t\t// Simulate next page reload and verify message persistence\n\t\t$this->session->cookie->reload();\n\t\t$this->session->native->reload();\n\t\t$this->assertEquals($cmsg, $this->session->cookie->flashdata($key));\n\t\t$this->assertEquals($nmsg, $this->session->native->flashdata($key));\n\n\t\t// Simulate next page reload and verify absence of messages\n\t\t$this->session->cookie->reload();\n\t\t$this->session->native->reload();\n\t\t$this->assertNull($this->session->cookie->flashdata($key));\n\t\t$this->assertNull($this->session->native->flashdata($key));\n\t}\n\n\tpublic function test_keep_flashdata_with_array()\n\t{\nreturn;\n\t\t// Set flashdata array for each driver\n\t\t$cdata = array(\n\t\t\t'one' => 'first',\n\t\t\t'two' => 'second',\n\t\t\t'three' => 'third',\n\t\t\t'foo' => 'bar',\n\t\t\t'bar' => 'baz'\n\t\t);\n\t\t$ndata = array(\n\t\t\t'one' => 'gold',\n\t\t\t'two' => 'silver',\n\t\t\t'three' => 'bronze',\n\t\t\t'foo' => 'baz',\n\t\t\t'bar' => 'foo'\n\t\t);\n\t\t$kdata = array(\n\t\t\t'one',\n\t\t\t'two',\n\t\t\t'three',\n\t\t\t'foo',\n\t\t\t'bar'\n\t\t);\n\t\t$this->session->cookie->set_flashdata($cdata);\n\t\t$this->session->native->set_flashdata($ndata);\n\n\t\t// Simulate page reload and verify independent messages\n\t\t$this->session->cookie->reload();\n\t\t$this->session->native->reload();\n\t\t$this->assertEquals($cdata, $this->session->cookie->flashdata());\n\t\t$this->assertEquals($ndata, $this->session->native->flashdata());\n\n\t\t// Keep messages\n\t\t$this->session->cookie->keep_flashdata($kdata);\n\t\t$this->session->native->keep_flashdata($kdata);\n\n\t\t// Simulate next page reload and verify message persistence\n\t\t$this->session->cookie->reload();\n\t\t$this->session->native->reload();\n\t\t$this->assertEquals($cdata, $this->session->cookie->flashdata());\n\t\t$this->assertEquals($ndata, $this->session->native->flashdata());\n\n\t\t// Simulate next page reload and verify absence of messages\n\t\t$this->session->cookie->reload();\n\t\t$this->session->native->reload();\n\t\t$this->assertEmpty($this->session->cookie->flashdata());\n\t\t$this->assertEmpty($this->session->native->flashdata());\n\t}\n\n\t/**\n\t * Test the all_flashdata() function\n\t */\n\tpublic function test_all_flashdata()\n\t{\nreturn;\n\t\t// Set a specific series of data for each driver\n\t\t$cdata = array(\n\t\t\t'one' => 'first',\n\t\t\t'two' => 'second',\n\t\t\t'three' => 'third',\n\t\t\t'foo' => 'bar',\n\t\t\t'bar' => 'baz'\n\t\t);\n\t\t$ndata = array(\n\t\t\t'one' => 'gold',\n\t\t\t'two' => 'silver',\n\t\t\t'three' => 'bronze',\n\t\t\t'foo' => 'baz',\n\t\t\t'bar' => 'foo'\n\t\t);\n\t\t$this->session->cookie->set_flashdata($cdata);\n\t\t$this->session->native->set_flashdata($ndata);\n\n\t\t// Simulate page reload and make sure all values are present\n\t\t$this->session->cookie->reload();\n\t\t$this->session->native->reload();\n\t\t$this->assertEquals($cdata, $this->session->cookie->flashdata());\n\t\t$this->assertEquals($ndata, $this->session->native->flashdata());\n\t}\n\n\t/**\n\t * Test the tempdata() functions\n\t */\n\tpublic function test_set_tempdata()\n\t{\nreturn;\n\t\t// Set tempdata message for each driver - 1 second timeout\n\t\t$key = 'tmptest';\n\t\t$cmsg = 'Some temp data';\n\t\t$this->session->cookie->set_tempdata($key, $cmsg, 1);\n\t\t$nmsg = 'Other temp data';\n\t\t$this->session->native->set_tempdata($key, $nmsg, 1);\n\n\t\t// Simulate page reload and verify independent messages\n\t\t$this->session->cookie->reload();\n\t\t$this->session->native->reload();\n\t\t$this->assertEquals($cmsg, $this->session->cookie->tempdata($key));\n\t\t$this->assertEquals($nmsg, $this->session->native->tempdata($key));\n\n\t\t// Wait 2 seconds, simulate page reload and verify message absence\n\t\tsleep(2);\n\t\t$this->session->cookie->reload();\n\t\t$this->session->native->reload();\n\t\t$this->assertNull($this->session->cookie->tempdata($key));\n\t\t$this->assertNull($this->session->native->tempdata($key));\n\t}\n\n\t/**\n\t * Test the unset_tempdata() function\n\t */\n\tpublic function test_unset_tempdata()\n\t{\nreturn;\n\t\t// Set tempdata message for each driver - 1 second timeout\n\t\t$key = 'utmptest';\n\t\t$cmsg = 'My temp data';\n\t\t$this->session->cookie->set_tempdata($key, $cmsg, 1);\n\t\t$nmsg = 'Your temp data';\n\t\t$this->session->native->set_tempdata($key, $nmsg, 1);\n\n\t\t// Verify independent messages\n\t\t$this->assertEquals($cmsg, $this->session->cookie->tempdata($key));\n\t\t$this->assertEquals($nmsg, $this->session->native->tempdata($key));\n\n\t\t// Unset data and verify message absence\n\t\t$this->session->cookie->unset_tempdata($key);\n\t\t$this->session->native->unset_tempdata($key);\n\t\t$this->assertNull($this->session->cookie->tempdata($key));\n\t\t$this->assertNull($this->session->native->tempdata($key));\n\t}\n\n\t/**\n\t * Test the sess_regenerate() function\n\t */\n\tpublic function test_sess_regenerate()\n\t{\nreturn;\n\t\t// Get current session id, regenerate, and compare\n\t\t// Cookie driver\n\t\t$oldid = $this->session->cookie->userdata('session_id');\n\t\t$this->session->cookie->sess_regenerate();\n\t\t$newid = $this->session->cookie->userdata('session_id');\n\t\t$this->assertNotEquals($oldid, $newid);\n\n\t\t// Native driver - bug #55267 (https://bugs.php.net/bug.php?id=55267) prevents testing this\n\t\t// $oldid = session_id();\n\t\t// $this->session->native->sess_regenerate();\n\t\t// $oldid = session_id();\n\t\t// $this->assertNotEquals($oldid, $newid);\n\t}\n\n\t/**\n\t * Test the sess_destroy() function\n\t */\n\tpublic function test_sess_destroy()\n\t{\nreturn;\n\t\t// Set a userdata message, destroy session, and verify absence\n\t\t$key = 'dsttest';\n\t\t$msg = 'More test data';\n\n\t\t// Cookie driver\n\t\t$this->session->cookie->set_userdata($key, $msg);\n\t\t$this->assertEquals($msg, $this->session->cookie->userdata($key));\n\t\t$this->session->cookie->sess_destroy();\n\t\t$this->assertNull($this->session->cookie->userdata($key));\n\n\t\t// Native driver\n\t\t$this->session->native->set_userdata($key, $msg);\n\t\t$this->assertEquals($msg, $this->session->native->userdata($key));\n\t\t$this->session->native->sess_destroy();\n\t\t$this->assertNull($this->session->native->userdata($key));\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/libraries/Table_test.php",
    "content": "<?php\n\nclass Table_test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t$this->table = new Mock_Libraries_Table();\n\t\t$this->ci_instance_var('table', $this->table);\n\t}\n\n\t// Setter Methods\n\t// --------------------------------------------------------------------\n\n\tpublic function test_set_template()\n\t{\n\t\t$this->assertFalse($this->table->set_template('not an array'));\n\n\t\t$template = array('a' => 'b');\n\n\t\t$this->table->set_template($template);\n\t\t$this->assertEquals($template, $this->table->template);\n\t}\n\n\tpublic function test_set_empty()\n\t{\n\t\t$this->table->set_empty('nada');\n\t\t$this->assertEquals('nada', $this->table->empty_cells);\n\t}\n\n\tpublic function test_set_caption()\n\t{\n\t\t$this->table->set_caption('awesome cap');\n\t\t$this->assertEquals('awesome cap', $this->table->caption);\n\t}\n\n\t/*\n\t * @depends\ttest_prep_args\n\t */\n\tpublic function test_set_heading()\n\t{\n\t\t// uses _prep_args internally, so we'll just do a quick\n\t\t// check to verify that func_get_args and prep_args are\n\t\t// being called.\n\n\t\t$this->table->set_heading('name', 'color', 'size');\n\n\t\t$this->assertEquals(\n\t\t\tarray(\n\t\t\t\tarray('data' => 'name'),\n\t\t\t\tarray('data' => 'color'),\n\t\t\t\tarray('data' => 'size')\n\t\t\t),\n\t\t\t$this->table->heading\n\t\t);\n\t}\n\n\t/*\n\t * @depends\ttest_prep_args\n\t */\n\tpublic function test_add_row()\n\t{\n\t\t// uses _prep_args internally, so we'll just do a quick\n\t\t// check to verify that func_get_args and prep_args are\n\t\t// being called.\n\n\t\t$this->table->add_row('my', 'pony', 'sings');\n\t\t$this->table->add_row('your', 'pony', 'stinks');\n\t\t$this->table->add_row('my pony', '>', 'your pony');\n\n\t\t$this->assertCount(3, $this->table->rows);\n\n\t\t$this->assertEquals(\n\t\t\tarray(\n\t\t\t\tarray('data' => 'your'),\n\t\t\t\tarray('data' => 'pony'),\n\t\t\t\tarray('data' => 'stinks')\n\t\t\t),\n\t\t\t$this->table->rows[1]\n\t\t);\n\t}\n\n\t// Uility Methods\n\t// --------------------------------------------------------------------\n\n\tpublic function test_prep_args()\n\t{\n\t\t$expected = array(\n\t\t\tarray('data' => 'name'),\n\t\t\tarray('data' => 'color'),\n\t\t\tarray('data' => 'size')\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\t$expected,\n\t\t\t$this->table->prep_args(array('name', 'color', 'size'))\n\t\t);\n\n\t\t// with cell attributes\n\t\t// need to add that new argument row to our expected outcome\n\t\t$expected[] = array('data' => 'weight', 'class' => 'awesome');\n\n\t\t$this->assertEquals(\n\t\t\t$expected,\n\t\t\t$this->table->prep_args(array('name', 'color', 'size', array('data' => 'weight', 'class' => 'awesome')))\n\t\t);\n\t}\n\n\tpublic function test_default_template_keys()\n\t{\n\t\t$keys = array(\n\t\t\t'table_open',\n\t\t\t'thead_open', 'thead_close',\n\t\t\t'heading_row_start', 'heading_row_end', 'heading_cell_start', 'heading_cell_end',\n\t\t\t'tbody_open', 'tbody_close',\n\t\t\t'row_start', 'row_end', 'cell_start', 'cell_end',\n\t\t\t'row_alt_start', 'row_alt_end', 'cell_alt_start', 'cell_alt_end',\n\t\t\t'table_close'\n\t\t);\n\n\t\tforeach ($keys as $key)\n\t\t{\n\t\t\t$this->assertArrayHasKey($key, $this->table->default_template());\n\t\t}\n\t}\n\n\tpublic function test_compile_template()\n\t{\n\t\t$this->assertFalse($this->table->set_template('invalid_junk'));\n\n\t\t// non default key\n\t\t$this->table->set_template(array('nonsense' => 'foo'));\n\t\t$this->table->compile_template();\n\n\t\t$this->assertArrayHasKey('nonsense', $this->table->template);\n\t\t$this->assertEquals('foo', $this->table->template['nonsense']);\n\n\t\t// override default\n\t\t$this->table->set_template(array('table_close' => '</table junk>'));\n\t\t$this->table->compile_template();\n\n\t\t$this->assertArrayHasKey('table_close', $this->table->template);\n\t\t$this->assertEquals('</table junk>', $this->table->template['table_close']);\n\t}\n\n\tpublic function test_make_columns()\n\t{\n\t\t// Test bogus parameters\n\t\t$this->assertFalse($this->table->make_columns('invalid_junk'));\n\t\t$this->assertFalse($this->table->make_columns(array()));\n\t\t$this->assertFalse($this->table->make_columns(array('one', 'two'), '2.5'));\n\n\t\t// Now on to the actual column creation\n\n\t\t$five_values = array(\n\t\t\t'Laura', 'Red', '15',\n\t\t\t'Katie', 'Blue'\n\t\t);\n\n\t\t// No column count - no changes to the array\n\t\t$this->assertEquals(\n\t\t\t$five_values,\n\t\t\t$this->table->make_columns($five_values)\n\t\t);\n\n\t\t// Column count of 3 leaves us with one &nbsp;\n\t\t$this->assertEquals(\n\t\t\tarray(\n\t\t\t\tarray('Laura', 'Red', '15'),\n\t\t\t\tarray('Katie', 'Blue', '&nbsp;')\n\t\t\t),\n\t\t\t$this->table->make_columns($five_values, 3)\n\t\t);\n\t}\n\n\tpublic function test_clear()\n\t{\n\t\t$this->table->set_heading('Name', 'Color', 'Size');\n\n\t\t// Make columns changes auto_heading\n\t\t$rows = $this->table->make_columns(array(\n\t\t\t'Laura', 'Red', '15',\n\t\t\t'Katie', 'Blue'\n\t\t), 3);\n\n\t\tforeach ($rows as $row)\n\t\t{\n\t\t\t$this->table->add_row($row);\n\t\t}\n\n\t\t$this->assertFalse($this->table->auto_heading);\n\t\t$this->assertCount(3, $this->table->heading);\n\t\t$this->assertCount(2, $this->table->rows);\n\n\t\t$this->table->clear();\n\n\t\t$this->assertTrue($this->table->auto_heading);\n\t\t$this->assertEmpty($this->table->heading);\n\t\t$this->assertEmpty($this->table->rows);\n\t}\n\n\tpublic function test_set_from_array()\n\t{\n\t\t$data = array(\n\t\t\tarray('name', 'color', 'number'),\n\t\t\tarray('Laura', 'Red', '22'),\n\t\t\tarray('Katie', 'Blue')\n\t\t);\n\n\t\t$this->table->auto_heading = FALSE;\n\t\t$this->table->set_from_array($data);\n\t\t$this->assertEmpty($this->table->heading);\n\n\t\t$this->table->clear();\n\n\t\t$this->table->set_from_array($data);\n\t\t$this->assertCount(2, $this->table->rows);\n\n\t\t$expected = array(\n\t\t\tarray('data' => 'name'),\n\t\t\tarray('data' => 'color'),\n\t\t\tarray('data' => 'number')\n\t\t);\n\n\t\t$this->assertEquals($expected, $this->table->heading);\n\n\t\t$expected = array(\n\t\t\tarray('data' => 'Katie'),\n\t\t\tarray('data' => 'Blue'),\n\t\t);\n\n\t\t$this->assertEquals($expected, $this->table->rows[1]);\n\t}\n\n\tpublic function test_set_from_object()\n\t{\n\t\t// This needs to be passed by reference to CI_DB_result::__construct()\n\t\t$dummy = new stdClass();\n\t\t$dummy->conn_id = NULL;\n\t\t$dummy->result_id = NULL;\n\n\t\t$db_result = new DB_result_dummy($dummy);\n\n\t\t$this->table->set_from_db_result($db_result);\n\n\t\t$expected = array(\n\t\t\tarray('data' => 'name'),\n\t\t\tarray('data' => 'email')\n\t\t);\n\n\t\t$this->assertEquals($expected, $this->table->heading);\n\n\t\t$expected = array(\n\t\t\t'name' => array('data' => 'Foo Bar'),\n\t\t\t'email' => array('data' => 'foo@bar.com'),\n\t\t);\n\n\t\t$this->assertEquals($expected, $this->table->rows[1]);\n\t}\n\n\tpublic function test_generate()\n\t{\n\t\t// Prepare the data\n\t\t$data = array(\n\t\t\tarray('Name', 'Color', 'Size'),\n\t\t\tarray('Fred', 'Blue', 'Small'),\n\t\t\tarray('Mary', 'Red', 'Large'),\n\t\t\tarray('John', 'Green', 'Medium')\n\t\t);\n\n\t\t$table = $this->table->generate($data);\n\n\t\t// Test the table header\n\t\t$this->assertEquals(1, substr_count($table, '<th>Name</th>'));\n\t\t$this->assertEquals(1, substr_count($table, '<th>Color</th>'));\n\t\t$this->assertEquals(1, substr_count($table, '<th>Size</th>'));\n\n\t\t// Test the first entry\n\t\t$this->assertEquals(1, substr_count($table, '<td>Fred</td>'));\n\t\t$this->assertEquals(1, substr_count($table, '<td>Blue</td>'));\n\t\t$this->assertEquals(1, substr_count($table, '<td>Small</td>'));\n\t}\n\n}\n\n// We need this for the _set_from_db_result() test\nclass DB_result_dummy extends CI_DB_result\n{\n\tpublic function list_fields()\n\t{\n\t\treturn array('name', 'email');\n\t}\n\n\tpublic function result_array()\n\t{\n\t\treturn array(\n\t\t\tarray('name' => 'John Doe', 'email' => 'john@doe.com'),\n\t\t\tarray('name' => 'Foo Bar', 'email' => 'foo@bar.com')\n\t\t);\n\t}\n}\n"
  },
  {
    "path": "tests/codeigniter/libraries/Typography_test.php",
    "content": "<?php\n\nclass Typography_test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t$this->type = new CI_Typography();\n\t\t$this->ci_instance('type', $this->type);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Tests the format_characters() function.\n\t *\n\t * this can and should grow.\n\t */\n\tpublic function test_format_characters()\n\t{\n\t\t$strs = array(\n\t\t\t'\"double quotes\"' \t\t\t\t=> '&#8220;double quotes&#8221;',\n\t\t\t'\"testing\" in \"theory\" that is' => '&#8220;testing&#8221; in &#8220;theory&#8221; that is',\n\t\t\t\"Here's what I'm\" \t\t\t\t=> 'Here&#8217;s what I&#8217;m',\n\t\t\t'&' \t\t\t\t\t\t\t=> '&amp;',\n\t\t\t'&amp;' \t\t\t\t\t\t=> '&amp;',\n\t\t\t'&nbsp;'\t\t\t\t\t\t=> '&nbsp;',\n\t\t\t'--'\t\t\t\t\t\t\t=> '&#8212;',\n\t\t\t'foo...'\t\t\t\t\t\t=> 'foo&#8230;',\n\t\t\t'foo..'\t\t\t\t\t\t\t=> 'foo..',\n\t\t\t'foo...bar.'\t\t\t\t\t=> 'foo&#8230;bar.',\n\t\t\t'test.  new'\t\t\t\t\t=> 'test.&nbsp; new',\n\t\t);\n\n\t\tforeach ($strs as $str => $expected)\n\t\t{\n\t\t\t$this->assertEquals($expected, $this->type->format_characters($str));\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_nl2br_except_pre()\n\t{\n\t\t$str = <<<EOH\nHello, I'm a happy string with some new lines.  \n\nI like to skip.\n\nJump\n\nand sing.\n\n<pre>\nI am inside a pre tag.  Please don't mess with me.\n\nk?\n</pre>\n\nThat's my story and I'm sticking to it.\n\nThe End.\nEOH;\n\n\t\t$expected = <<<EOH\nHello, I'm a happy string with some new lines.  <br />\n<br />\nI like to skip.<br />\n<br />\nJump<br />\n<br />\nand sing.<br />\n<br />\n<pre>\nI am inside a pre tag.  Please don't mess with me.\n\nk?\n</pre><br />\n<br />\nThat's my story and I'm sticking to it.<br />\n<br />\nThe End.\nEOH;\n\n\t\t$this->assertEquals($expected, $this->type->nl2br_except_pre($str));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_auto_typography()\n\t{\n\t\t$this->_blank_string();\n\t\t$this->_standardize_new_lines();\n\t\t$this->_reduce_linebreaks();\n\t\t$this->_remove_comments();\n\t\t$this->_protect_pre();\n\t\t$this->_no_opening_block();\n\t\t$this->_protect_braced_quotes();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tprivate function _blank_string()\n\t{\n\t\t// Test blank string\n\t\t$this->assertEquals('', $this->type->auto_typography(''));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tprivate function _standardize_new_lines()\n\t{\n\t\t$strs = array(\n\t\t\t\"My string\\rhas return characters\"\t=> \"<p>My string<br />\\nhas return characters</p>\",\n\t\t\t'This one does not!' \t\t\t\t=> '<p>This one does not!</p>'\n\t\t);\n\n\t\tforeach ($strs as $str => $expect)\n\t\t{\n\t\t\t$this->assertEquals($expect, $this->type->auto_typography($str));\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tprivate function _reduce_linebreaks()\n\t{\n\t\t$str = \"This has way too many linebreaks.\\n\\n\\n\\nSee?\";\n\t\t$expect = \"<p>This has way too many linebreaks.</p>\\n\\n<p>See?</p>\";\n\n\t\t$this->assertEquals($expect, $this->type->auto_typography($str, TRUE));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tprivate function _remove_comments()\n\t{\n\t\t$str = '<!-- I can haz comments? -->  But no!';\n\t\t$expect = '<p><!-- I can haz comments? -->&nbsp; But no!</p>';\n\n\t\t$this->assertEquals($expect, $this->type->auto_typography($str));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tprivate function _protect_pre()\n\t{\n\t\t$str = '<p>My Sentence</p><pre>var_dump($this);</pre>';\n\t\t$expect = '<p>My Sentence</p><pre>var_dump($this);</pre>';\n\n\t\t$this->assertEquals($expect, $this->type->auto_typography($str));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tprivate function _no_opening_block()\n\t{\n\t\t$str = 'My Sentence<pre>var_dump($this);</pre>';\n\t\t$expect = '<p>My Sentence</p><pre>var_dump($this);</pre>';\n\n\t\t$this->assertEquals($expect, $this->type->auto_typography($str));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function _protect_braced_quotes()\n\t{\n\t\t$this->type->protect_braced_quotes = TRUE;\n\n\t\t$str = 'Test {parse=\"foobar\"}';\n\t\t$expect = '<p>Test {parse=\"foobar\"}</p>';\n\n\t\t$this->assertEquals($expect, $this->type->auto_typography($str));\n\n\t\t$this->type->protect_braced_quotes = FALSE;\n\n\t\t$str = 'Test {parse=\"foobar\"}';\n\t\t$expect = '<p>Test {parse=&#8220;foobar&#8221;}</p>';\n\n\t\t$this->assertEquals($expect, $this->type->auto_typography($str));\n\t}\n\n}"
  },
  {
    "path": "tests/codeigniter/libraries/Upload_test.php",
    "content": "<?php\n\nclass Upload_test extends CI_TestCase {\n\n\tpublic function set_up()\n\t{\n\t\t$ci = $this->ci_instance();\n\t\t$ci->upload = new CI_Upload();\n\t\t$ci->security = new Mock_Core_Security('UTF-8');\n\t\t$ci->lang = $this->getMockBuilder('CI_Lang')->setMethods(array('load', 'line'))->getMock();\n\t\t$ci->lang->expects($this->any())->method('line')->will($this->returnValue(FALSE));\n\t\t$this->upload = $ci->upload;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test___construct_initialize()\n\t{\n\t\t// via __construct\n\n\t\t$upload = new CI_Upload(\n\t\t\tarray(\n\t\t\t\t'file_name' => 'foo',\n\t\t\t\t'file_ext_tolower' => TRUE\n\t\t\t)\n\t\t);\n\n\t\t$reflection = new ReflectionClass($upload);\n\t\t$reflection = $reflection->getProperty('_file_name_override');\n\t\t$reflection->setAccessible(TRUE);\n\t\t$this->assertEquals('foo', $reflection->getValue($upload));\n\n\t\t$this->assertTrue($upload->file_ext_tolower);\n\n\t\t// reset (defaults to true)\n\n\t\t$upload->initialize(array('file_name' => 'bar'));\n\t\t$this->assertEquals('bar', $upload->file_name);\n\t\t$this->assertFalse($upload->file_ext_tolower);\n\n\t\t// no reset\n\n\t\t$upload->initialize(array('file_ext_tolower' => TRUE), FALSE);\n\t\t$this->assertTrue($upload->file_ext_tolower);\n\t\t$this->assertEquals('bar', $upload->file_name);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_do_upload()\n\t{\n\t\t$this->markTestSkipped(\"We can't test this at the moment because of the call to is_uploaded_file() in do_upload() which isn't supported by vfsStream.\");\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tfunction test_data()\n\t{\n\t\t$data = array(\n\t\t\t\t'file_name'\t\t=> 'hello.txt',\n\t\t\t\t'file_type'\t\t=> 'text/plain',\n\t\t\t\t'file_path'\t\t=> '/tmp/',\n\t\t\t\t'full_path'\t\t=> '/tmp/hello.txt',\n\t\t\t\t'raw_name'\t\t=> 'hello',\n\t\t\t\t'orig_name'\t\t=> 'hello.txt',\n\t\t\t\t'client_name'\t\t=> '',\n\t\t\t\t'file_ext'\t\t=> '.txt',\n\t\t\t\t'file_size'\t\t=> 100,\n\t\t\t\t'is_image'\t\t=> FALSE,\n\t\t\t\t'image_width'\t\t=> '',\n\t\t\t\t'image_height'\t\t=> '',\n\t\t\t\t'image_type'\t\t=> '',\n\t\t\t\t'image_size_str'\t=> ''\n\t\t\t);\n\n\t\t$this->upload->set_upload_path('/tmp/');\n\n\t\tforeach ($data as $k => $v)\n\t\t{\n\t\t\t$this->upload->{$k}\t= $v;\n\t\t}\n\n\t\t$this->assertEquals('hello.txt', $this->upload->data('file_name'));\n\t\t$this->assertEquals($data, $this->upload->data());\n\t}\n\n\tfunction test_set_upload_path()\n\t{\n\t\t$this->upload->set_upload_path('/tmp/');\n\t\t$this->assertEquals('/tmp/', $this->upload->upload_path);\n\n\t\t$this->upload->set_upload_path('/tmp');\n\t\t$this->assertEquals('/tmp/', $this->upload->upload_path);\n\t}\n\n\tfunction test_set_filename()\n\t{\n\t\t$dir = 'uploads';\n\t\t$isnew = 'helloworld.txt';\n\t\t$exists = 'hello-world.txt';\n\t\t$this->ci_vfs_create($exists, 'Hello world.', $this->ci_app_root, $dir);\n\t\t$path = $this->ci_vfs_path($dir.'/', APPPATH);\n\t\t$this->upload->file_ext = '.txt';\n\n\t\t$this->assertEquals($isnew, $this->upload->set_filename($path, $isnew));\n\t\t$this->assertEquals('hello-world1.txt', $this->upload->set_filename($path, $exists));\n\t}\n\n\tfunction test_set_max_filesize()\n\t{\n\t\t$this->upload->set_max_filesize(100);\n\t\t$this->assertEquals(100, $this->upload->max_size);\n\t}\n\n\tfunction test_set_max_filename()\n\t{\n\t\t$this->upload->set_max_filename(100);\n\t\t$this->assertEquals(100, $this->upload->max_filename);\n\t}\n\n\tfunction test_set_max_width()\n\t{\n\t\t$this->upload->set_max_width(100);\n\t\t$this->assertEquals(100, $this->upload->max_width);\n\t}\n\n\tfunction test_set_max_height()\n\t{\n\t\t$this->upload->set_max_height(100);\n\t\t$this->assertEquals(100, $this->upload->max_height);\n\t}\n\n\tfunction test_set_allowed_types()\n\t{\n\t\t$this->upload->set_allowed_types('*');\n\t\t$this->assertEquals('*', $this->upload->allowed_types);\n\n\t\t$this->upload->set_allowed_types('foo|bar');\n\t\t$this->assertEquals(array('foo', 'bar'), $this->upload->allowed_types);\n\t}\n\n\tfunction test_set_image_properties()\n\t{\n\t\t$this->upload->file_type = 'image/gif';\n\t\t$this->upload->file_temp = realpath(PROJECT_BASE.'tests/mocks/uploads/ci_logo.gif');\n\n\t\t$props = array(\n\t\t\t'image_width'\t=>\t170,\n\t\t\t'image_height'\t=>\t73,\n\t\t\t'image_type'\t=>\t'gif',\n\t\t\t'image_size_str'\t=>\t'width=\"170\" height=\"73\"'\n\t\t);\n\n\t\t$this->upload->set_image_properties($this->upload->file_temp);\n\n\t\t$this->assertEquals($props['image_width'], $this->upload->image_width);\n\t\t$this->assertEquals($props['image_height'], $this->upload->image_height);\n\t\t$this->assertEquals($props['image_type'], $this->upload->image_type);\n\t\t$this->assertEquals($props['image_size_str'], $this->upload->image_size_str);\n\t}\n\n\tfunction test_set_xss_clean()\n\t{\n\t\t$this->upload->set_xss_clean(TRUE);\n\t\t$this->assertTrue($this->upload->xss_clean);\n\n\t\t$this->upload->set_xss_clean(FALSE);\n\t\t$this->assertFalse($this->upload->xss_clean);\n\t}\n\n\tfunction test_is_image()\n\t{\n\t\t$this->upload->file_type = 'image/x-png';\n\t\t$this->assertTrue($this->upload->is_image());\n\n\t\t$this->upload->file_type = 'text/plain';\n\t\t$this->assertFalse($this->upload->is_image());\n\t}\n\n\tfunction test_is_allowed_filetype()\n\t{\n\t\t$this->upload->allowed_types = array('html', 'gif');\n\n\t\t$this->upload->file_ext = '.txt';\n\t\t$this->upload->file_type = 'text/plain';\n\t\t$this->assertFalse($this->upload->is_allowed_filetype(FALSE));\n\t\t$this->assertFalse($this->upload->is_allowed_filetype(TRUE));\n\n\t\t$this->upload->file_ext = '.html';\n\t\t$this->upload->file_type = 'text/html';\n\t\t$this->assertTrue($this->upload->is_allowed_filetype(FALSE));\n\t\t$this->assertTrue($this->upload->is_allowed_filetype(TRUE));\n\n\t\t$this->upload->file_temp = realpath(PROJECT_BASE.'tests/mocks/uploads/ci_logo.gif');\n\t\t$this->upload->file_ext = '.gif';\n\t\t$this->upload->file_type = 'image/gif';\n\t\t$this->assertTrue($this->upload->is_allowed_filetype());\n\t}\n\n\tfunction test_is_allowed_filesize()\n\t{\n\t\t$this->upload->max_size = 100;\n\t\t$this->upload->file_size = 99;\n\n\t\t$this->assertTrue($this->upload->is_allowed_filesize());\n\n\t\t$this->upload->file_size = 101;\n\t\t$this->assertFalse($this->upload->is_allowed_filesize());\n\t}\n\n\tfunction test_is_allowed_dimensions()\n\t{\n\t\t$this->upload->file_type = 'text/plain';\n\t\t$this->assertTrue($this->upload->is_allowed_dimensions());\n\n\t\t$this->upload->file_type = 'image/gif';\n\t\t$this->upload->file_temp = realpath(PROJECT_BASE.'tests/mocks/uploads/ci_logo.gif');\n\n\t\t$this->upload->max_width = 10;\n\t\t$this->assertFalse($this->upload->is_allowed_dimensions());\n\n\t\t$this->upload->max_width = 170;\n\t\t$this->upload->max_height = 10;\n\t\t$this->assertFalse($this->upload->is_allowed_dimensions());\n\n\t\t$this->upload->max_height = 73;\n\t\t$this->assertTrue($this->upload->is_allowed_dimensions());\n\t}\n\n\tfunction test_validate_upload_path()\n\t{\n\t\t$this->upload->upload_path = '';\n\t\t$this->assertFalse($this->upload->validate_upload_path());\n\n\t\t$dir = 'uploads';\n\t\t$this->ci_vfs_mkdir($dir);\n\t\t$this->upload->upload_path = $this->ci_vfs_path($dir);\n\t\t$this->assertTrue($this->upload->validate_upload_path());\n\t}\n\n\tfunction test_get_extension()\n\t{\n\t\t$this->assertEquals('.txt', $this->upload->get_extension('hello.txt'));\n\t\t$this->assertEquals('.htaccess', $this->upload->get_extension('.htaccess'));\n\t\t$this->assertEquals('', $this->upload->get_extension('hello'));\n\t}\n\n\tfunction test_limit_filename_length()\n\t{\n\t\t$this->assertEquals('hello.txt', $this->upload->limit_filename_length('hello.txt', 10));\n\t\t$this->assertEquals('hello.txt', $this->upload->limit_filename_length('hello-world.txt', 9));\n\t}\n\n\tfunction test_do_xss_clean()\n\t{\n\t\t$dir = 'uploads';\n\t\t$file1 = 'file1.txt';\n\t\t$file2 = 'file2.txt';\n\t\t$file3 = 'file3.txt';\n\t\t$this->ci_vfs_create($file1, 'The billy goat was waiting for them.', $this->ci_vfs_root, $dir);\n\t\t$this->ci_vfs_create($file2, '', $this->ci_vfs_root, $dir);\n\t\t$this->ci_vfs_create($file3, '<script type=\"text/javascript\">alert(\"Boo! said the billy goat\")</script>', $this->ci_vfs_root, $dir);\n\n\t\t$this->upload->file_temp = $this->ci_vfs_path($file1, $dir);\n\t\t$this->assertTrue($this->upload->do_xss_clean());\n\n\t\t$this->upload->file_temp = $this->ci_vfs_path($file2, $dir);\n\t\t$this->assertFalse($this->upload->do_xss_clean());\n\n\t\t$this->upload->file_temp = $this->ci_vfs_path($file3, $dir);\n\t\t$this->assertFalse($this->upload->do_xss_clean());\n\n\t\t$this->upload->file_temp = realpath(PROJECT_BASE.'tests/mocks/uploads/ci_logo.gif');\n\t\t$this->assertTrue($this->upload->do_xss_clean());\n\t}\n\n\tfunction test_set_error()\n\t{\n\t\t$errors = array(\n\t\t\t'An error!'\n\t\t);\n\n\t\t$another = 'Another error!';\n\n\t\t$this->upload->set_error($errors);\n\t\t$this->assertEquals($errors, $this->upload->error_msg);\n\n\t\t$errors[] = $another;\n\t\t$this->upload->set_error($another);\n\t\t$this->assertEquals($errors, $this->upload->error_msg);\n\t}\n\n\tfunction test_display_errors()\n\t{\n\t\t$this->upload->error_msg[] = 'Error test';\n\t\t$this->assertEquals('<p>Error test</p>', $this->upload->display_errors());\n\t}\n\n}\n"
  },
  {
    "path": "tests/codeigniter/libraries/Useragent_test.php",
    "content": "<?php\n\nclass UserAgent_test extends CI_TestCase {\n\n\tprotected $_user_agent = 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-us) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27';\n\tprotected $_mobile_ua = 'Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_1 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8B117 Safari/6531.22.7';\n\n\tpublic function set_up()\n\t{\n\t\t// set a baseline user agent\n\t\t$_SERVER['HTTP_USER_AGENT'] = $this->_user_agent;\n\n\t\t$this->ci_vfs_clone('application/config/user_agents.php');\n\t\t$this->agent = new CI_User_agent();\n\t\t$this->ci_instance_var('agent', $this->agent);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_accept_lang()\n\t{\n\t\t$_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'en';\n\t\t$this->assertTrue($this->agent->accept_lang());\n\t\tunset($_SERVER['HTTP_ACCEPT_LANGUAGE']);\n\t\t$this->assertTrue($this->agent->accept_lang('en'));\n\t\t$this->assertFalse($this->agent->accept_lang('fr'));\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_mobile()\n\t{\n\t\t// Mobile Not Set\n\t\t$_SERVER['HTTP_USER_AGENT'] = $this->_mobile_ua;\n\t\t$this->assertEquals('', $this->agent->mobile());\n\t\tunset($_SERVER['HTTP_USER_AGENT']);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_is_functions()\n\t{\n\t\t$this->assertTrue($this->agent->is_browser());\n\t\t$this->assertTrue($this->agent->is_browser('Safari'));\n\t\t$this->assertFalse($this->agent->is_browser('Firefox'));\n\t\t$this->assertFalse($this->agent->is_robot());\n\t\t$this->assertFalse($this->agent->is_mobile());\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_referrer()\n\t{\n\t\t$_SERVER['HTTP_REFERER'] = 'http://codeigniter.com/userguide3/';\n\t\t$this->assertTrue($this->agent->is_referral());\n\t\t$this->assertEquals('http://codeigniter.com/userguide3/', $this->agent->referrer());\n\n\t\t$this->agent->referer = NULL;\n\t\tunset($_SERVER['HTTP_REFERER']);\n\t\t$this->assertFalse($this->agent->is_referral());\n\t\t$this->assertEquals('', $this->agent->referrer());\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_agent_string()\n\t{\n\t\t$this->assertEquals($this->_user_agent, $this->agent->agent_string());\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_browser_info()\n\t{\n\t\t$this->assertEquals('Mac OS X', $this->agent->platform());\n\t\t$this->assertEquals('Safari', $this->agent->browser());\n\t\t$this->assertEquals('533.20.27', $this->agent->version());\n\t\t$this->assertEquals('', $this->agent->robot());\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function test_charsets()\n\t{\n\t\t$_SERVER['HTTP_ACCEPT_CHARSET'] = 'utf8';\n\t\t$this->agent->charsets = array();\n\t\t$this->agent->charsets();\n\t\t$this->assertTrue($this->agent->accept_charset('utf8'));\n\t\t$this->assertFalse($this->agent->accept_charset('foo'));\n\t\t$this->assertEquals('utf8', $this->agent->charsets[0]);\n\n\t\t$_SERVER['HTTP_ACCEPT_CHARSET'] = '';\n\t\t$this->agent->charsets = array();\n\t\t$this->assertFalse($this->agent->accept_charset());\n\t\t$this->assertEquals('Undefined', $this->agent->charsets[0]);\n\n\t\t$_SERVER['HTTP_ACCEPT_CHARSET'] = 'iso-8859-5, unicode-1-1; q=0.8';\n\t\t$this->agent->charsets = array();\n\t\t$this->assertTrue($this->agent->accept_charset('iso-8859-5'));\n\t\t$this->assertTrue($this->agent->accept_charset('unicode-1-1'));\n\t\t$this->assertFalse($this->agent->accept_charset('foo'));\n\t\t$this->assertEquals('iso-8859-5', $this->agent->charsets[0]);\n\t\t$this->assertEquals('unicode-1-1', $this->agent->charsets[1]);\n\n\t\tunset($_SERVER['HTTP_ACCEPT_CHARSET']);\n\t}\n\n\tpublic function test_parse()\n\t{\n\t\t$new_agent = 'Mozilla/5.0 (Android; Mobile; rv:13.0) Gecko/13.0 Firefox/13.0';\n\t\t$this->agent->parse($new_agent);\n\n\t\t$this->assertEquals('Android', $this->agent->platform());\n\t\t$this->assertEquals('Firefox', $this->agent->browser());\n\t\t$this->assertEquals('13.0', $this->agent->version());\n\t\t$this->assertEquals('', $this->agent->robot());\n\t\t$this->assertEquals('Android', $this->agent->mobile());\n\t\t$this->assertEquals($new_agent, $this->agent->agent_string());\n\t\t$this->assertTrue($this->agent->is_browser());\n\t\t$this->assertFalse($this->agent->is_robot());\n\t\t$this->assertTrue($this->agent->is_mobile());\n\t\t$this->assertTrue($this->agent->is_mobile('android'));\n\t}\n\n}"
  },
  {
    "path": "tests/mocks/autoloader.php",
    "content": "<?php\n\n// This autoloader provide convenient way to working with mock object\n// make the test looks natural. This autoloader support cascade file loading as well\n// within mocks directory.\n//\n// Prototype :\n//\n// $mock_table = new Mock_Libraries_Table(); \t\t\t// Will load ./mocks/libraries/table.php\n// $mock_database_driver = new Mock_Database_Driver();\t// Will load ./mocks/database/driver.php\n// and so on...\nfunction autoload($class)\n{\n\t$dir = realpath(dirname(__FILE__)).DIRECTORY_SEPARATOR;\n\n\t$ci_core = array(\n\t\t'Benchmark',\n\t\t'Config',\n\t\t'Controller',\n\t\t'Exceptions',\n\t\t'Hooks',\n\t\t'Input',\n\t\t'Lang',\n\t\t'Loader',\n\t\t'Log',\n\t\t'Model',\n\t\t'Output',\n\t\t'Router',\n\t\t'Security',\n\t\t'URI',\n\t\t'Utf8'\n\t);\n\n\t$ci_libraries = array(\n\t\t'Calendar',\n\t\t'Driver_Library',\n\t\t'Email',\n\t\t'Encrypt',\n\t\t'Encryption',\n\t\t'Form_validation',\n\t\t'Ftp',\n\t\t'Image_lib',\n\t\t'Javascript',\n\t\t'Migration',\n\t\t'Pagination',\n\t\t'Parser',\n\t\t'Profiler',\n\t\t'Table',\n\t\t'Trackback',\n\t\t'Typography',\n\t\t'Unit_test',\n\t\t'Upload',\n\t\t'User_agent',\n\t\t'Xmlrpc',\n\t\t'Zip'\n\t);\n\n\t$ci_drivers = array('Session', 'Cache');\n\n\tif (strpos($class, 'Mock_') === 0)\n\t{\n\t\t$class = strtolower(str_replace(array('Mock_', '_'), array('', DIRECTORY_SEPARATOR), $class));\n\t}\n\telseif (strpos($class, 'CI_') === 0)\n\t{\n\t\t$subclass = substr($class, 3);\n\n\t\tif (in_array($subclass, $ci_core))\n\t\t{\n\t\t\t$dir = SYSTEM_PATH.'core'.DIRECTORY_SEPARATOR;\n\t\t\t$class = $subclass;\n\t\t}\n\t\telseif (in_array($subclass, $ci_libraries))\n\t\t{\n\t\t\t$dir = SYSTEM_PATH.'libraries'.DIRECTORY_SEPARATOR;\n\t\t\t$class = ($subclass === 'Driver_Library') ? 'Driver' : $subclass;\n\t\t}\n\t\telseif (in_array($subclass, $ci_drivers))\n\t\t{\n\t\t\t$dir = SYSTEM_PATH.'libraries'.DIRECTORY_SEPARATOR.$subclass.DIRECTORY_SEPARATOR;\n\t\t\t$class = $subclass;\n\t\t}\n\t\telseif (in_array(($parent = strtok($subclass, '_')), $ci_drivers)) {\n\t\t\t$dir = SYSTEM_PATH.'libraries'.DIRECTORY_SEPARATOR.$parent.DIRECTORY_SEPARATOR.'drivers'.DIRECTORY_SEPARATOR;\n\t\t\t$class = $subclass;\n\t\t}\n\t\telseif (preg_match('/^CI_DB_(.+)_(.+)_(driver|forge|result|utility)$/', $class, $m) && count($m) === 4)\n\t\t{\n\t\t\t$driver_path = SYSTEM_PATH.'database'.DIRECTORY_SEPARATOR.'drivers'.DIRECTORY_SEPARATOR;\n\t\t\t$dir = $driver_path.$m[1].DIRECTORY_SEPARATOR.'subdrivers'.DIRECTORY_SEPARATOR;\n\t\t\t$file = $dir.$m[1].'_'.$m[2].'_'.$m[3].'.php';\n\t\t}\n\t\telseif (preg_match('/^CI_DB_(.+)_(driver|forge|result|utility)$/', $class, $m) && count($m) === 3)\n\t\t{\n\t\t\t$driver_path = SYSTEM_PATH.'database'.DIRECTORY_SEPARATOR.'drivers'.DIRECTORY_SEPARATOR;\n\t\t\t$dir = $driver_path.$m[1].DIRECTORY_SEPARATOR;\n\t\t\t$file = $dir.$m[1].'_'.$m[2].'.php';\n\t\t}\n\t\telseif (strpos($class, 'CI_DB') === 0)\n\t\t{\n\t\t\t$dir = SYSTEM_PATH.'database'.DIRECTORY_SEPARATOR;\n\t\t\t$file = $dir.str_replace(array('CI_DB','active_record'), array('DB', 'active_rec'), $subclass).'.php';\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$class = strtolower($class);\n\t\t}\n\t}\n\n\t$file = isset($file) ? $file : $dir.$class.'.php';\n\n\tif ( ! file_exists($file))\n\t{\n\t\treturn FALSE;\n\t}\n\n\tinclude_once($file);\n}\n"
  },
  {
    "path": "tests/mocks/ci_testcase.php",
    "content": "<?php\n\nclass CI_TestCase extends \\PHPUnit\\Framework\\TestCase {\n\n\tpublic $ci_vfs_root;\n\tpublic $ci_app_root;\n\tpublic $ci_base_root;\n\tpublic $ci_readonly_dir;\n\tprotected $ci_instance;\n\tprotected static $ci_test_instance;\n\n\tprivate $global_map = array(\n\t\t'benchmark'\t=> 'bm',\n\t\t'config'\t=> 'cfg',\n\t\t'hooks'\t\t=> 'ext',\n\t\t'utf8'\t\t=> 'uni',\n\t\t'router'\t=> 'rtr',\n\t\t'output'\t=> 'out',\n\t\t'security'\t=> 'sec',\n\t\t'input'\t\t=> 'in',\n\t\t'lang'\t\t=> 'lang',\n\t\t'loader'\t=> 'load',\n\t\t'model'\t\t=> 'model'\n\t);\n\n\t// --------------------------------------------------------------------\n\n\tpublic function __construct($name = null, array $data = array(), $dataName = '')\n\t{\n\t\tparent::__construct($name, $data, $dataName);\n\t\t$this->ci_instance = new stdClass();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function setUp()\n\t{\n\t\t// Setup VFS with base directories\n\t\t$this->ci_vfs_root = vfsStream::setup('');\n\t\t$this->ci_app_root = vfsStream::newDirectory('application')->at($this->ci_vfs_root);\n\t\t$this->ci_base_root = vfsStream::newDirectory('system')->at($this->ci_vfs_root);\n\t\t$this->ci_view_root = vfsStream::newDirectory('views')->at($this->ci_app_root);\n\t\t$this->ci_readonly_dir = vfsStream::newDirectory('readonly', 555)->at($this->ci_app_root);\n\n\t\tif (method_exists($this, 'set_up'))\n\t\t{\n\t\t\t$this->set_up();\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function tearDown()\n\t{\n\t\tif (method_exists($this, 'tear_down'))\n\t\t{\n\t\t\t$this->tear_down();\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic static function instance()\n\t{\n\t\treturn self::$ci_test_instance;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function ci_set_config($key = '', $val = '')\n\t{\n\t\t// Add test config\n\t\tif ( ! isset($this->ci_instance->config))\n\t\t{\n\t\t\t$this->ci_instance->config = new CI_TestConfig();\n\t\t}\n\n\t\t// Empty key means just do setup above\n\t\tif ($key === '')\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tif (is_array($key))\n\t\t{\n\t\t\t$this->ci_instance->config->config = $key;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$this->ci_instance->config->config[$key] = $val;\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function ci_get_config()\n\t{\n\t\treturn isset($this->ci_instance->config) ? $this->ci_instance->config->config : array();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function ci_instance($obj = FALSE)\n\t{\n\t\tif ( ! is_object($obj))\n\t\t{\n\t\t\treturn $this->ci_instance;\n\t\t}\n\n\t\t$this->ci_instance = $obj;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function ci_instance_var($name, $obj = FALSE)\n\t{\n\t\tif ( ! is_object($obj))\n\t\t{\n\t\t\treturn $this->ci_instance->$name;\n\t\t}\n\n\t\t$this->ci_instance->$name =& $obj;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Grab a core class\n\t *\n\t * Loads the correct core class without extensions\n\t * and returns a reference to the class name in the\n\t * globals array with the correct key. This way the\n\t * test can modify the variable it assigns to and\n\t * still maintain the global.\n\t */\n\tpublic function &ci_core_class($name)\n\t{\n\t\t$name = strtolower($name);\n\n\t\tif (isset($this->global_map[$name]))\n\t\t{\n\t\t\t$class_name = ucfirst($name);\n\t\t\t$global_name = $this->global_map[$name];\n\t\t}\n\t\telseif (in_array($name, $this->global_map))\n\t\t{\n\t\t\t$class_name = ucfirst(array_search($name, $this->global_map));\n\t\t\t$global_name = $name;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthrow new Exception('Not a valid core class.');\n\t\t}\n\n\t\tif ( ! class_exists('CI_'.$class_name))\n\t\t{\n\t\t\trequire_once SYSTEM_PATH.'core/'.$class_name.'.php';\n\t\t}\n\n\t\t$GLOBALS[strtoupper($global_name)] = 'CI_'.$class_name;\n\t\treturn $GLOBALS[strtoupper($global_name)];\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t// convenience function for global mocks\n\tpublic function ci_set_core_class($name, $obj)\n\t{\n\t\t$orig =& $this->ci_core_class($name);\n\t\t$orig = $obj;\n\t}\n\n\t/**\n\t * Create VFS directory\n\t *\n\t * @param\tstring\tDirectory name\n\t * @param\tobject\tOptional root to create in\n\t * @return\tobject\tNew directory object\n\t */\n\tpublic function ci_vfs_mkdir($name, $root = NULL)\n\t{\n\t\t// Check for root\n\t\tif ( ! $root)\n\t\t{\n\t\t\t$root = $this->ci_vfs_root;\n\t\t}\n\n\t\t// Return new directory object\n\t\treturn vfsStream::newDirectory($name)->at($root);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Create VFS content\n\t *\n\t * @param\tstring\tFile name\n\t * @param\tstring\tFile content\n\t * @param\tobject\tVFS directory object\n\t * @param\tmixed\tOptional subdirectory path or array of subs\n\t * @return\tvoid\n\t */\n\tpublic function ci_vfs_create($file, $content = '', $root = NULL, $path = NULL)\n\t{\n\t\t// Check for array\n\t\tif (is_array($file))\n\t\t{\n\t\t\tforeach ($file as $name => $content)\n\t\t\t{\n\t\t\t\t$this->ci_vfs_create($name, $content, $root, $path);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Assert .php extension if none given\n\t\tif (pathinfo($file, PATHINFO_EXTENSION) == '')\n\t\t{\n\t\t\t$file .= '.php';\n\t\t}\n\n\t\t// Build content\n\t\t$tree = array($file => $content);\n\n\t\t// Check for path\n\t\t$subs = array();\n\t\tif ($path)\n\t\t{\n\t\t\t// Explode if not array\n\t\t\t$subs = is_array($path) ? $path : explode('/', trim($path, '/'));\n\t\t}\n\n\t\t// Check for root\n\t\tif ( ! $root)\n\t\t{\n\t\t\t// Use base VFS root\n\t\t\t$root = $this->ci_vfs_root;\n\t\t}\n\n\t\t// Handle subdirectories\n\t\twhile (($dir = array_shift($subs)))\n\t\t{\n\t\t\t// See if subdir exists under current root\n\t\t\t$dir_root = $root->getChild($dir);\n\t\t\tif ($dir_root)\n\t\t\t{\n\t\t\t\t// Yes - recurse into subdir\n\t\t\t\t$root = $dir_root;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// No - put subdirectory back and quit\n\t\t\t\tarray_unshift($subs, $dir);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Create any remaining subdirectories\n\t\tif ($subs)\n\t\t{\n\t\t\tforeach (array_reverse($subs) as $dir)\n\t\t\t{\n\t\t\t\t// Wrap content in subdirectory for creation\n\t\t\t\t$tree = array($dir => $tree);\n\t\t\t}\n\t\t}\n\n\t\t// Create tree\n\t\tvfsStream::create($tree, $root);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Clone a real file into VFS\n\t *\n\t * @param\tstring\tPath from base directory\n\t * @return\tbool\tTRUE on success, otherwise FALSE\n\t */\n\tpublic function ci_vfs_clone($path, $dest='')\n\t{\n\t\t// Check for array\n\t\tif (is_array($path))\n\t\t{\n\t\t\tforeach ($path as $file)\n\t\t\t{\n\t\t\t\t$this->ci_vfs_clone($file, $dest);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Get real file contents\n\t\t$content = file_get_contents(PROJECT_BASE.$path);\n\t\tif ($content === FALSE)\n\t\t{\n\t\t\t// Couldn't find file to clone\n\t\t\treturn FALSE;\n\t\t}\n\n\t\tif (empty($dest))\n\t\t{\n\t\t\t$dest = dirname($path);\n\t\t}\n\n\t\t$this->ci_vfs_create(basename($path), $content, NULL, $dest);\n\t\treturn TRUE;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Helper to get a VFS URL path\n\t *\n\t * @param\tstring\tPath\n\t * @param\tstring\tOptional base path\n\t * @return\tstring\tPath URL\n\t */\n\tpublic function ci_vfs_path($path, $base = '')\n\t{\n\t\t// Check for base path\n\t\tif ($base)\n\t\t{\n\t\t\t// Prepend to path\n\t\t\t$path = rtrim($base, '/').'/'.ltrim($path, '/');\n\n\t\t\t// Is it already in URL form?\n\t\t\tif (strpos($path, '://') !== FALSE)\n\t\t\t{\n\t\t\t\t// Done - return path\n\t\t\t\treturn $path;\n\t\t\t}\n\t\t}\n\n\t\t// Trim leading slash and return URL\n\t\treturn vfsStream::url(ltrim($path, '/'));\n\t}\n\n\t// --------------------------------------------------------------------\n\t// Internals\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * Overwrite runBare\n\t *\n\t * PHPUnit instantiates the test classes before\n\t * running them individually. So right before a test\n\t * runs we set our instance. Normally this step would\n\t * happen in setUp, but someone is bound to forget to\n\t * call the parent method and debugging this is no fun.\n\t */\n\tpublic function runBare()\n\t{\n\t\tself::$ci_test_instance = $this;\n\t\tparent::runBare();\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function helper($name)\n\t{\n\t\trequire_once(SYSTEM_PATH.'helpers/'.$name.'_helper.php');\n\t}\n\n\t// --------------------------------------------------------------------\n\n\tpublic function lang($name)\n\t{\n\t\trequire(SYSTEM_PATH.'language/english/'.$name.'_lang.php');\n\t\treturn $lang;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * This overload is useful to create a stub, that need to have a specific method.\n\t */\n\tpublic function __call($method, $args)\n\t{\n\t\tif ($this->{$method} instanceof Closure)\n\t\t{\n\t\t\treturn call_user_func_array($this->{$method},$args);\n\t\t}\n\n\t\treturn parent::__call($method, $args);\n\t}\n\n\tpublic function setExpectedException($exception_class, $exception_message = '', $exception_code = null)\n\t{\n\t\t$use_expect_exception = method_exists($this, 'expectException');\n\n\t\tif ($use_expect_exception)\n\t\t{\n\t\t\t$this->expectException($exception_class);\n\t\t\t$exception_message !== '' && $this->expectExceptionMessage($exception_message);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tparent::setExpectedException($exception_class, $exception_message, $exception_code);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tests/mocks/ci_testconfig.php",
    "content": "<?php\n\nclass CI_TestConfig extends CI_Config {\n\n\tpublic $config = array();\n\tpublic $_config_paths = array(APPPATH);\n\tpublic $loaded = array();\n\n\tpublic function item($key, $index = '')\n\t{\n\t\treturn isset($this->config[$key]) ? $this->config[$key] : FALSE;\n\t}\n\n\tpublic function load($file = '', $use_sections = FALSE, $fail_gracefully = FALSE)\n\t{\n\t\t$this->loaded[] = $file;\n\t\treturn TRUE;\n\t}\n\n}\n"
  },
  {
    "path": "tests/mocks/core/common.php",
    "content": "<?php\n\n// Set up the global CI functions in their most minimal core representation\n\nif ( ! function_exists('get_instance'))\n{\n\tfunction &get_instance()\n\t{\n\t\t$test = CI_TestCase::instance();\n\t\t$test = $test->ci_instance();\n\t\treturn $test;\n\t}\n}\n\n// --------------------------------------------------------------------\n\nif ( ! function_exists('get_config'))\n{\n\tfunction &get_config()\n\t{\n\t\t$test = CI_TestCase::instance();\n\t\t$config = $test->ci_get_config();\n\t\treturn $config;\n\t}\n}\n\nif ( ! function_exists('config_item'))\n{\n\tfunction config_item($item)\n\t{\n\t\t$config =& get_config();\n\n\t\tif ( ! isset($config[$item]))\n\t\t{\n\t\t\treturn NULL;\n\t\t}\n\n\t\treturn $config[$item];\n\t}\n}\n\nif ( ! function_exists('get_mimes'))\n{\n\t/**\n\t * Returns the MIME types array from config/mimes.php\n\t *\n\t * @return\tarray\n\t */\n\tfunction &get_mimes()\n\t{\n\t\tstatic $_mimes = array();\n\n\t\tif (empty($_mimes))\n\t\t{\n\t\t\t$path = realpath(PROJECT_BASE.'application/config/mimes.php');\n\t\t\tif (is_file($path))\n\t\t\t{\n\t\t\t\t$_mimes = include($path);\n\t\t\t}\n\t\t}\n\n\t\treturn $_mimes;\n\t}\n}\n\n// --------------------------------------------------------------------\n\n/*\nif ( ! function_exists('load_class'))\n{\n\tfunction load_class($class, $directory = 'libraries', $prefix = 'CI_')\n\t{\n\t\tif ($directory !== 'core' OR $prefix !== 'CI_')\n\t\t{\n\t\t\tthrow new Exception('Not Implemented: Non-core load_class()');\n\t\t}\n\n\t\t$test = CI_TestCase::instance();\n\n\t\t$obj =& $test->ci_core_class($class);\n\n\t\tif (is_string($obj))\n\t\t{\n\t\t\tthrow new Exception('Bad Isolation: Use ci_set_core_class to set '.$class);\n\t\t}\n\n\t\treturn $obj;\n\t}\n}\n*/\n\n// Clean up error messages\n// --------------------------------------------------------------------\n\nif ( ! function_exists('show_error'))\n{\n\tfunction show_error($message, $status_code = 500, $heading = 'An Error Was Encountered')\n\t{\n\t\tthrow new RuntimeException('CI Error: '.$message);\n\t}\n}\n\nif ( ! function_exists('show_404'))\n{\n\tfunction show_404($page = '', $log_error = TRUE)\n\t{\n\t\tthrow new RuntimeException('CI Error: 404');\n\t}\n}\n\nif ( ! function_exists('_exception_handler'))\n{\n\tfunction _exception_handler($severity, $message, $filepath, $line)\n\t{\n\t\tthrow new RuntimeException('CI Exception: '.$message.' | '.$filepath.' | '.$line);\n\t}\n}\n\n// We assume a few things about our environment ...\n// --------------------------------------------------------------------\nif ( ! function_exists('is_loaded'))\n{\n\tfunction &is_loaded()\n\t{\n\t\t$loaded = array();\n\t\treturn $loaded;\n\t}\n}\n\nif ( ! function_exists('log_message'))\n{\n\tfunction log_message($level, $message)\n\t{\n\t\treturn TRUE;\n\t}\n}\n\nif ( ! function_exists('set_status_header'))\n{\n\tfunction set_status_header($code = 200, $text = '')\n\t{\n\t\treturn TRUE;\n\t}\n}\n\nif ( ! function_exists('is_cli'))\n{\n\t// In order to test HTTP functionality, we need to lie about this\n\tfunction is_cli()\n\t{\n\t\treturn FALSE;\n\t}\n}"
  },
  {
    "path": "tests/mocks/core/security.php",
    "content": "<?php\n\nclass Mock_Core_Security extends CI_Security {\n\n\tpublic function csrf_set_cookie()\n\t{\n\t\t// We cannot set cookie in CLI mode, so for csrf test, who rely on $_COOKIE,\n\t\t// we superseded set_cookie with directly set the cookie variable,\n\t\t// @see : ./tests/codeigniter/core/Security_test.php, line 8\n\t\treturn $this;\n\t}\n\n\t// Override inaccessible protected properties\n\tpublic function __get($property)\n\t{\n\t\treturn isset($this->{'_'.$property}) ? $this->{'_'.$property} : NULL;\n\t}\n\n\tpublic function remove_evil_attributes($str, $is_image)\n\t{\n\t\treturn $this->_remove_evil_attributes($str, $is_image);\n\t}\n\n\t// Override inaccessible protected method\n\tpublic function __call($method, $params)\n\t{\n\t\tif (is_callable(array($this, '_'.$method)))\n\t\t{\n\t\t\treturn call_user_func_array(array($this, '_'.$method), $params);\n\t\t}\n\n\t\tthrow new BadMethodCallException('Method '.$method.' was not found');\n\t}\n\n}\n"
  },
  {
    "path": "tests/mocks/core/uri.php",
    "content": "<?php\n\nclass Mock_Core_URI extends CI_URI {\n\n\tpublic function __construct()\n\t{\n\t\t$test = CI_TestCase::instance();\n\t\t$cls =& $test->ci_core_class('cfg');\n\n\t\t// set predictable config values\n\t\t$test->ci_set_config(array(\n\t\t\t'index_page'\t\t=> 'index.php',\n\t\t\t'base_url'\t\t=> 'http://example.com/',\n\t\t\t'subclass_prefix'\t=> 'MY_',\n\t\t\t'enable_query_strings'\t=> FALSE,\n\t\t\t'permitted_uri_chars'\t=> 'a-z 0-9~%.:_\\-'\n\t\t));\n\n\t\t$this->config = new $cls;\n\n\t\tif ($this->config->item('enable_query_strings') !== TRUE OR is_cli())\n\t\t{\n\t\t\t$this->_permitted_uri_chars = $this->config->item('permitted_uri_chars');\n\t\t}\n\t}\n\n\tpublic function _set_permitted_uri_chars($value)\n\t{\n\t\t$this->_permitted_uri_chars = $value;\n\t}\n\n}"
  },
  {
    "path": "tests/mocks/database/config/mysql.php",
    "content": "<?php\n\nreturn array(\n\n\t// Typical Database configuration\n\t'mysql' => array(\n\t\t'dsn' => '',\n\t\t'hostname' => '127.0.0.1',\n\t\t'username' => 'travis',\n\t\t'password' => 'travis',\n\t\t'database' => 'ci_test',\n\t\t'dbdriver' => 'mysql'\n\t),\n\n\t// Database configuration with failover\n\t'mysql_failover' => array(\n\t\t'dsn' => '',\n\t\t'hostname' => '127.0.0.1',\n\t\t'username' => 'not_travis',\n\t\t'password' => 'wrong password',\n\t\t'database' => 'not_ci_test',\n\t\t'dbdriver' => 'mysql',\n\t\t'failover' => array(\n\t\t\tarray(\n\t\t\t\t'dsn' => '',\n\t\t\t\t'hostname' => '127.0.0.1',\n\t\t\t\t'username' => 'travis',\n\t\t\t\t'password' => 'travis',\n\t\t\t\t'database' => 'ci_test',\n\t\t\t\t'dbdriver' => 'mysql',\n\t\t\t)\n\t\t)\n\t)\n);"
  },
  {
    "path": "tests/mocks/database/config/mysqli.php",
    "content": "<?php\n\nreturn array(\n\n\t// Typical Database configuration\n\t'mysqli' => array(\n\t\t'dsn' => '',\n\t\t'hostname' => '127.0.0.1',\n\t\t'username' => 'travis',\n\t\t'password' => 'travis',\n\t\t'database' => 'ci_test',\n\t\t'dbdriver' => 'mysqli'\n\t),\n\n\t// Database configuration with failover\n\t'mysqli_failover' => array(\n\t\t'dsn' => '',\n\t\t'hostname' => '127.0.0.1',\n\t\t'username' => 'not_travis',\n\t\t'password' => 'wrong password',\n\t\t'database' => 'not_ci_test',\n\t\t'dbdriver' => 'mysqli',\n\t\t'failover' => array(\n\t\t\tarray(\n\t\t\t\t'dsn' => '',\n\t\t\t\t'hostname' => '127.0.0.1',\n\t\t\t\t'username' => 'travis',\n\t\t\t\t'password' => 'travis',\n\t\t\t\t'database' => 'ci_test',\n\t\t\t\t'dbdriver' => 'mysqli',\n\t\t\t)\n\t\t)\n\t)\n);"
  },
  {
    "path": "tests/mocks/database/config/pdo/mysql.php",
    "content": "<?php\n\nreturn array(\n\n\t// Typical Database configuration\n\t'pdo/mysql' => array(\n\t\t'dsn' => 'mysql:host=127.0.0.1;dbname=ci_test',\n\t\t'hostname' => '127.0.0.1',\n\t\t'username' => 'travis',\n\t\t'password' => 'travis',\n\t\t'database' => 'ci_test',\n\t\t'dbdriver' => 'pdo',\n\t\t'subdriver' => 'mysql'\n\t),\n\n\t// Database configuration with failover\n\t'pdo/mysql_failover' => array(\n\t\t'dsn' => '',\n\t\t'hostname' => '127.0.0.1',\n\t\t'username' => 'not_travis',\n\t\t'password' => 'wrong password',\n\t\t'database' => 'not_ci_test',\n\t\t'dbdriver' => 'pdo',\n\t\t'subdriver' => 'mysql',\n\t\t'failover' => array(\n\t\t\tarray(\n\t\t\t\t'dsn' => 'mysql:host=127.0.0.1;dbname=ci_test',\n\t\t\t\t'hostname' => '127.0.0.1',\n\t\t\t\t'username' => 'travis',\n\t\t\t\t'password' => 'travis',\n\t\t\t\t'database' => 'ci_test',\n\t\t\t\t'dbdriver' => 'pdo',\n\t\t\t\t'subdriver' => 'mysql'\n\t\t\t)\n\t\t)\n\t)\n);"
  },
  {
    "path": "tests/mocks/database/config/pdo/pgsql.php",
    "content": "<?php\n\nreturn array(\n\n\t// Typical Database configuration\n\t'pdo/pgsql' => array(\n\t\t'dsn' => 'pgsql:host=localhost;port=5432;dbname=ci_test;',\n\t\t'hostname' => 'localhost',\n\t\t'username' => 'postgres',\n\t\t'password' => 'postgres',\n\t\t'database' => 'ci_test',\n\t\t'dbdriver' => 'pdo',\n\t\t'subdriver' => 'pgsql'\n\t),\n\n\t// Database configuration with failover\n\t'pdo/pgsql_failover' => array(\n\t\t'dsn' => '',\n\t\t'hostname' => 'localhost',\n\t\t'username' => 'not_travis',\n\t\t'password' => 'wrong password',\n\t\t'database' => 'not_ci_test',\n\t\t'dbdriver' => 'pdo',\n\t\t'subdriver' => 'pgsql',\n\t\t'failover' => array(\n\t\t\tarray(\n\t\t\t\t'dsn' => 'pgsql:host=localhost;port=5432;dbname=ci_test;',\n\t\t\t\t'hostname' => 'localhost',\n\t\t\t\t'username' => 'postgres',\n\t\t\t\t'password' => 'postgres',\n\t\t\t\t'database' => 'ci_test',\n\t\t\t\t'dbdriver' => 'pdo',\n\t\t\t\t'subdriver' => 'pgsql'\n\t\t\t)\n\t\t)\n\t)\n);"
  },
  {
    "path": "tests/mocks/database/config/pdo/sqlite.php",
    "content": "<?php\n\nreturn array(\n\n\t// Typical Database configuration\n\t'pdo/sqlite' => array(\n\t\t'dsn' => 'sqlite:/'.realpath(__DIR__.'/../..').'/ci_test.sqlite',\n\t\t'hostname' => 'localhost',\n\t\t'username' => 'sqlite',\n\t\t'password' => 'sqlite',\n\t\t'database' => 'sqlite',\n\t\t'dbdriver' => 'pdo',\n\t\t'subdriver' => 'sqlite'\n\t),\n\n\t// Database configuration with failover\n\t'pdo/sqlite_failover' => array(\n\t\t'dsn' => 'sqlite:not_exists.sqlite',\n\t\t'hostname' => 'localhost',\n\t\t'username' => 'sqlite',\n\t\t'password' => 'sqlite',\n\t\t'database' => 'sqlite',\n\t\t'dbdriver' => 'pdo',\n\t\t'subdriver' => 'sqlite',\n\t\t'failover' => array(\n\t\t\tarray(\n\t\t\t\t'dsn' => 'sqlite:/'.realpath(__DIR__.'/../..').'/ci_test.sqlite',\n\t\t\t\t'hostname' => 'localhost',\n\t\t\t\t'username' => 'sqlite',\n\t\t\t\t'password' => 'sqlite',\n\t\t\t\t'database' => 'sqlite',\n\t\t\t\t'dbdriver' => 'pdo',\n\t\t\t\t'subdriver' => 'sqlite'\n\t\t\t)\n\t\t)\n\t)\n);"
  },
  {
    "path": "tests/mocks/database/config/pgsql.php",
    "content": "<?php\n\nreturn array(\n\n\t// Typical Database configuration\n\t'pgsql' => array(\n\t\t'dsn' => '',\n\t\t'hostname' => 'localhost',\n\t\t'username' => 'postgres',\n\t\t'password' => 'postgres',\n\t\t'database' => 'ci_test',\n\t\t'dbdriver' => 'postgre'\n\t),\n\n\t// Database configuration with failover\n\t'pgsql_failover' => array(\n\t\t'dsn' => '',\n\t\t'hostname' => 'localhost',\n\t\t'username' => 'not_travis',\n\t\t'password' => 'wrong password',\n\t\t'database' => 'not_ci_test',\n\t\t'dbdriver' => 'postgre',\n\t\t'failover' => array(\n\t\t\tarray(\n\t\t\t\t'dsn' => '',\n\t\t\t\t'hostname' => 'localhost',\n\t\t\t\t'username' => 'postgres',\n\t\t\t\t'password' => 'postgres',\n\t\t\t\t'database' => 'ci_test',\n\t\t\t\t'dbdriver' => 'postgre',\n\t\t\t)\n\t\t)\n\t)\n);"
  },
  {
    "path": "tests/mocks/database/config/sqlite.php",
    "content": "<?php\n\nreturn array(\n\n\t// Typical Database configuration\n\t'sqlite' => array(\n\t\t'dsn' => '',\n\t\t'hostname' => 'localhost',\n\t\t'username' => 'sqlite',\n\t\t'password' => 'sqlite',\n\t\t'database' => realpath(__DIR__.'/..').'/ci_test.sqlite',\n\t\t'dbdriver' => 'sqlite3'\n\t),\n\n\t// Database configuration with failover\n\t'sqlite_failover' => array(\n\t\t'dsn' => '',\n\t\t'hostname' => 'localhost',\n\t\t'username' => 'sqlite',\n\t\t'password' => 'sqlite',\n\t\t'database' => '../not_exists.sqlite',\n\t\t'dbdriver' => 'sqlite3',\n\t\t'failover' => array(\n\t\t\tarray(\n\t\t\t\t'dsn' => '',\n\t\t\t\t'hostname' => 'localhost',\n\t\t\t\t'username' => 'sqlite',\n\t\t\t\t'password' => 'sqlite',\n\t\t\t\t'database' => realpath(__DIR__.'/..').'/ci_test.sqlite',\n\t\t\t\t'dbdriver' => 'sqlite3'\n\t\t\t)\n\t\t)\n\t)\n);"
  },
  {
    "path": "tests/mocks/database/db/driver.php",
    "content": "<?php\n\nclass Mock_Database_DB_Driver extends CI_DB_driver {\n\n\t/**\n\t * @var object The actual Driver\n\t */\n\tprotected $ci_db_driver;\n\n\t/**\n\t * Instantiate the database driver\n\t *\n\t * @param  string \tDB Driver class name\n\t * @param  array \tDB configuration to set\n\t * @return void\n\t */\n\tpublic function __construct($driver_class, $config = array())\n\t{\n\t\tif (is_string($driver_class))\n\t\t{\n\t\t\t$this->ci_db_driver = new $driver_class($config);\n\t\t}\n\t}\n\n\t/**\n\t * Overloading method, emulate the actual driver method (multiple inheritance workaround)\n\t */\n\tpublic function __call($method, $arguments)\n\t{\n\t\tif ( ! is_callable(array($this->ci_db_driver, $method)))\n\t\t{\n\t\t\tthrow new BadMethodCallException($method. ' not exists or not implemented');\n\t\t}\n\n\t\treturn call_user_func_array(array($this->ci_db_driver, $method), $arguments);\n\t}\n\n}\n\nclass CI_DB extends CI_DB_query_builder {}"
  },
  {
    "path": "tests/mocks/database/db.php",
    "content": "<?php\n\nclass Mock_Database_DB {\n\n\t/**\n\t * @var array DB configuration\n\t */\n\tprivate $config = array();\n\n\t/**\n\t * @var string DB driver name\n\t */\n\tprivate static $dbdriver = '';\n\n\t/**\n\t * @var string DB sub-driver name\n\t */\n\tprivate static $subdriver = '';\n\n\t/**\n\t * Prepare database configuration skeleton\n\t *\n\t * @param  array \tDB configuration to set\n\t * @return void\n\t */\n\tpublic function __construct($config = array())\n\t{\n\t\t$this->config = $config;\n\t}\n\n\t/**\n\t * Build DSN connection string for DB driver instantiate process\n\t *\n\t * @param \tstring \tGroup name\n\t * @return \tstring \tDSN Connection string\n\t */\n\tpublic function set_dsn($group = 'default')\n\t{\n\t\tif ( ! isset($this->config[$group]))\n\t\t{\n\t\t\tthrow new InvalidArgumentException('Group '.$group.' not exists');\n\t\t}\n\n\t\tself::$dbdriver = $this->config[$group]['dbdriver'];\n\t\tif (isset($this->config[$group]['subdriver']))\n\t\t{\n\t\t\tself::$subdriver = $this->config[$group]['subdriver'];\n\t\t}\n\n\t\t$params = array(\n\t\t\t'dbprefix' => '',\n\t\t\t'pconnect' => FALSE,\n\t\t\t'db_debug' => FALSE,\n\t\t\t'cache_on' => FALSE,\n\t\t\t'cachedir' => '',\n\t\t\t'char_set' => 'utf8',\n\t\t\t'dbcollat' => 'utf8_general_ci',\n\t\t\t'swap_pre' => '',\n\t\t\t'stricton' => FALSE\n\t\t);\n\n\t\t$config = array_merge($this->config[$group], $params);\n\t\t$dsnstring = empty($config['dsn']) ? FALSE : $config['dsn'];\n\t\t$subdriver = empty($config['subdriver']) ? FALSE: $config['subdriver'];\n\t\t$failover = empty($config['failover']) ? FALSE : $config['failover'];\n\n\t\t$dsn = $config['dbdriver'].'://'.$config['username'].':'.$config['password']\n\t\t\t\t\t.'@'.$config['hostname'].'/'.$config['database'];\n\n\t\t// Build the parameter\n\t\t$other_params = array_slice($config, 6);\n\t\tif ($dsnstring) $other_params['dsn'] = $dsnstring;\n\t\tif ($subdriver) $other_params['subdriver'] = $subdriver;\n\t\tif ($failover) $other_params['failover'] = $failover;\n\n\t\treturn $dsn.'?'.http_build_query($other_params);\n\t}\n\n\t/**\n\t * Return a database config array\n\t *\n\t * @see \t./config\n\t * @param\tstring\tDriver based configuration\n\t * @return\tarray\n\t */\n\tpublic static function config($driver)\n\t{\n\t\t$dir = realpath(dirname(__FILE__)).DIRECTORY_SEPARATOR;\n\t\treturn include($dir.'config'.DIRECTORY_SEPARATOR.$driver.'.php');\n\t}\n\n\t/**\n\t * Main DB method wrapper\n\t *\n\t * @param \tstring\tGroup or DSN string\n\t * @param \tbool\n\t * @return \tobject\n\t */\n\tpublic static function DB($group, $query_builder = FALSE)\n\t{\n\t\t// Create dummy driver and builder files to \"load\" - the mocks have\n\t\t// already triggered autoloading of the real files\n\t\t$case = CI_TestCase::instance();\n\t\t$driver = self::$dbdriver;\n\t\t$subdriver = self::$subdriver;\n\t\t$case->ci_vfs_create(array(\n\t\t\t'DB_driver.php' => '',\n\t\t\t'DB_result.php' => '',\n\t\t\t'DB_forge.php' => '',\n\t\t\t'DB_query_builder.php' => ''\n\t\t), '', $case->ci_base_root, 'database');\n\t\tif (file_exists(SYSTEM_PATH.'database/drivers/'.$driver.'/'.$driver.'_driver.php'))\n\t\t{\n\t\t\t$case->ci_vfs_create(array(\n\t\t\t\t$driver.'_driver.php' => '',\n\t\t\t\t$driver.'_result.php' => '',\n\t\t\t\t$driver.'_forge.php' => ''\n\t\t\t), '', $case->ci_base_root, 'database/drivers/'.$driver);\n\t\t}\n\t\tif ($subdriver)\n\t\t{\n\t\t\t$case->ci_vfs_create(array(\n\t\t\t\t$driver.'_'.$subdriver.'_driver.php' => '',\n\t\t\t\t$driver.'_'.$subdriver.'_forge.php' => ''\n\t\t\t), '', $case->ci_base_root, 'database/drivers/'.$driver.'/subdrivers');\n\t\t}\n\n\t\tinclude_once(SYSTEM_PATH.'database/DB.php');\n\n\t\ttry\n\t\t{\n\t\t\t$db = DB($group, $query_builder);\n\t\t}\n\t\tcatch (Exception $e)\n\t\t{\n\t\t\tthrow new RuntimeException($e->getMessage());\n\t\t}\n\n\t\treturn $db;\n\t}\n\n}"
  },
  {
    "path": "tests/mocks/database/drivers/mysql.php",
    "content": "<?php\n\nclass Mock_Database_Drivers_Mysql extends Mock_Database_DB_Driver {\n\n\t/**\n\t * Instantiate the database driver\n\t *\n\t * @param\tarray\tDB configuration to set\n\t * @return\tvoid\n\t */\n\tpublic function __construct($config = array())\n\t{\n\t\tparent::__construct('CI_DB_mysql_driver', $config);\n\t}\n\n}"
  },
  {
    "path": "tests/mocks/database/drivers/mysqli.php",
    "content": "<?php\n\nclass Mock_Database_Drivers_Mysqli extends Mock_Database_DB_Driver {\n\n\t/**\n\t * Instantiate the database driver\n\t *\n\t * @param\tarray\tDB configuration to set\n\t * @return\tvoid\n\t */\n\tpublic function __construct($config = array())\n\t{\n\t\tparent::__construct('CI_DB_mysqli_driver', $config);\n\t}\n\n}"
  },
  {
    "path": "tests/mocks/database/drivers/pdo.php",
    "content": "<?php\n\nclass Mock_Database_Drivers_PDO extends Mock_Database_DB_Driver {\n\n\t/**\n\t * Instantiate the database driver\n\t *\n\t * @param\tarray\tDB configuration to set\n\t * @return\tvoid\n\t */\n\tpublic function __construct($config = array())\n\t{\n\t\tparent::__construct('CI_DB_pdo_driver', $config);\n\t}\n}"
  },
  {
    "path": "tests/mocks/database/drivers/postgre.php",
    "content": "<?php\n\nclass Mock_Database_Drivers_Postgre extends Mock_Database_DB_Driver {\n\n\t/**\n\t * Instantiate the database driver\n\t *\n\t * @param\tarray\tDB configuration to set\n\t * @return\tvoid\n\t */\n\tpublic function __construct($config = array())\n\t{\n\t\tparent::__construct('CI_DB_postgre_driver', $config);\n\t}\n\n}"
  },
  {
    "path": "tests/mocks/database/drivers/sqlite.php",
    "content": "<?php\n\nclass Mock_Database_Drivers_Sqlite extends Mock_Database_DB_Driver {\n\n\t/**\n\t * Instantiate the database driver\n\t *\n\t * @param\tarray\tDB configuration to set\n\t * @return\tvoid\n\t */\n\tpublic function __construct($config = array())\n\t{\n\t\tparent::__construct('CI_DB_sqlite3_driver', $config);\n\t}\n\n}"
  },
  {
    "path": "tests/mocks/database/schema/skeleton.php",
    "content": "<?php\n\nclass Mock_Database_Schema_Skeleton {\n\n\t/**\n\t * @var object Database Holder\n\t */\n\tpublic static $db;\n\n\t/**\n\t * @var object Forge Holder\n\t */\n\tpublic static $forge;\n\n\t/**\n\t * @var object Driver Holder\n\t */\n\tpublic static $driver;\n\n\t/**\n\t * Initialize both database and forge components\n\t */\n\tpublic static function init($driver)\n\t{\n\t\tif (empty(self::$db) && empty(self::$forge))\n\t\t{\n\t\t\t// E_DEPRECATED notices thrown by mysql_connect(), mysql_pconnect()\n\t\t\t// on PHP 5.5+ cause the tests to fail\n\t\t\tif ($driver === 'mysql' && version_compare(PHP_VERSION, '5.5', '>='))\n\t\t\t{\n\t\t\t\terror_reporting(E_ALL & ~E_DEPRECATED);\n\t\t\t}\n\n\t\t\t$config = Mock_Database_DB::config($driver);\n\t\t\t$connection = new Mock_Database_DB($config);\n\t\t\t$db = Mock_Database_DB::DB($connection->set_dsn($driver), TRUE);\n\n\t\t\tCI_TestCase::instance()->ci_instance_var('db', $db);\n\n\t\t\t$loader = new CI_Loader();\n\t\t\t$loader->dbforge();\n\t\t\t$forge = CI_TestCase::instance()->ci_instance_var('dbforge');\n\n\t\t\tself::$db = $db;\n\t\t\tself::$forge = $forge;\n\t\t\tself::$driver = $driver;\n\t\t}\n\n\t\treturn self::$db;\n\t}\n\n\t/**\n\t * Create the dummy tables\n\t *\n\t * @return void\n\t */\n\tpublic static function create_tables()\n\t{\n\t\t// User Table\n\t\tself::$forge->add_field(array(\n\t\t\t'id' => array(\n\t\t\t\t'type' => 'INTEGER',\n\t\t\t\t'constraint' => 3\n\t\t\t),\n\t\t\t'name' => array(\n\t\t\t\t'type' => 'VARCHAR',\n\t\t\t\t'constraint' => 40\n\t\t\t),\n\t\t\t'email' => array(\n\t\t\t\t'type' => 'VARCHAR',\n\t\t\t\t'constraint' => 100\n\t\t\t),\n\t\t\t'country' => array(\n\t\t\t\t'type' => 'VARCHAR',\n\t\t\t\t'constraint' => 40\n\t\t\t)\n\t\t));\n\t\tself::$forge->add_key('id', TRUE);\n\t\tself::$forge->create_table('user', TRUE) OR show_error('Unable to create the `user` table');\n\n\t\t// Job Table\n\t\tself::$forge->add_field(array(\n\t\t\t'id' => array(\n\t\t\t\t'type' => 'INTEGER',\n\t\t\t\t'constraint' => 3\n\t\t\t),\n\t\t\t'name' => array(\n\t\t\t\t'type' => 'VARCHAR',\n\t\t\t\t'constraint' => 40\n\t\t\t),\n\t\t\t'description' => array(\n\t\t\t\t'type' => 'TEXT'\n\t\t\t)\n\t\t));\n\t\tself::$forge->add_key('id', TRUE);\n\t\tself::$forge->create_table('job', TRUE) OR show_error('Unable to create the `job` table');\n\n\t\t// Misc Table\n\t\tself::$forge->add_field(array(\n\t\t\t'id' => array(\n\t\t\t\t'type' => 'INTEGER',\n\t\t\t\t'constraint' => 3\n\t\t\t),\n\t\t\t'key' => array(\n\t\t\t\t'type' => 'VARCHAR',\n\t\t\t\t'constraint' => 40\n\t\t\t),\n\t\t\t'value' => array(\n\t\t\t\t'type' => 'TEXT'\n\t\t\t)\n\t\t));\n\t\tself::$forge->add_key('id', TRUE);\n\t\tself::$forge->create_table('misc', TRUE) OR show_error('Unable to create the `misc` table');\n\t}\n\n\t/**\n\t * Create the dummy datas\n\t *\n\t * @return void\n\t */\n\tpublic static function create_data()\n\t{\n\t\t// Job Data\n\t\t$data = array(\n\t\t\t'user' => array(\n\t\t\t\tarray('id' => 1, 'name' => 'Derek Jones', 'email' => 'derek@world.com', 'country' => 'US'),\n\t\t\t\tarray('id' => 2, 'name' => 'Ahmadinejad', 'email' => 'ahmadinejad@world.com', 'country' => 'Iran'),\n\t\t\t\tarray('id' => 3, 'name' => 'Richard A Causey', 'email' => 'richard@world.com', 'country' => 'US'),\n\t\t\t\tarray('id' => 4, 'name' => 'Chris Martin', 'email' => 'chris@world.com', 'country' => 'UK')\n\t\t\t),\n\t\t\t'job' => array(\n\t\t\t\tarray('id' => 1, 'name' => 'Developer', 'description' => 'Awesome job, but sometimes makes you bored'),\n\t\t\t\tarray('id' => 2, 'name' => 'Politician', 'description' => 'This is not really a job'),\n\t\t\t\tarray('id' => 3, 'name' => 'Accountant', 'description' => 'Boring job, but you will get free snack at lunch'),\n\t\t\t\tarray('id' => 4, 'name' => 'Musician', 'description' => 'Only Coldplay can actually called Musician')\n\t\t\t),\n\t\t\t'misc' => array(\n\t\t\t\tarray('id' => 1, 'key' => '\\\\xxxfoo456', 'value' => 'Entry with \\\\xxx'),\n\t\t\t\tarray('id' => 2, 'key' => '\\\\%foo456', 'value' => 'Entry with \\\\%'),\n\t\t\t\tarray('id' => 3, 'key' => 'spaces and tabs', 'value' => ' One  two   three\ttab')\n\t\t\t)\n\t\t);\n\n\t\tforeach ($data as $table => $dummy_data)\n\t\t{\n\t\t\tself::$db->truncate($table) OR show_error(\"Unable to truncate `{$table}` table\");\n\n\t\t\tforeach ($dummy_data as $single_dummy_data)\n\t\t\t{\n\t\t\t\tself::$db->insert($table, $single_dummy_data) OR show_error(\"Unable to insert data into `{$table}` table\");\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "tests/mocks/libraries/driver.php",
    "content": "<?php\n\n/**\n * Mock library to subclass Driver for testing\n */\nclass Mock_Libraries_Driver extends CI_Driver_Library {\n\t/**\n\t * Set valid drivers list\n\t */\n\tpublic function driver_list($drivers = NULL)\n\t{\n\t\tif (empty($drivers))\n\t\t{\n\t\t\treturn $this->valid_drivers;\n\t\t}\n\n\t\t$this->valid_drivers = (array) $drivers;\n\t}\n\n\t/**\n\t * Get library name\n\t */\n\tpublic function get_name()\n\t{\n\t\treturn $this->lib_name;\n\t}\n}"
  },
  {
    "path": "tests/mocks/libraries/encryption.php",
    "content": "<?php\n\nclass Mock_Libraries_Encryption extends CI_Encryption {\n\n\t/**\n\t * __get_params()\n\t *\n\t * Allows public calls to the otherwise protected _get_params().\n\t */\n\tpublic function __get_params($params)\n\t{\n\t\treturn $this->_get_params($params);\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * get_key()\n\t *\n\t * Allows checking for key changes.\n\t */\n\tpublic function get_key()\n\t{\n\t\treturn $this->_key;\n\t}\n\n\t// --------------------------------------------------------------------\n\n\t/**\n\t * __driver_get_handle()\n\t *\n\t * Allows checking for _mcrypt_get_handle(), _openssl_get_handle()\n\t */\n\tpublic function __driver_get_handle($driver, $cipher, $mode)\n\t{\n\t\treturn $this->{'_'.$driver.'_get_handle'}($cipher, $mode);\n\t}\n\n}"
  },
  {
    "path": "tests/mocks/libraries/session.php",
    "content": "<?php\n\n/**\n * Mock library to add testing features to Session driver library\n */\nclass Mock_Libraries_Session extends CI_Session {\n\t/**\n\t * Simulate new page load\n\t */\n\tpublic function reload()\n\t{\n\t\t$this->_flashdata_sweep();\n\t\t$this->_flashdata_mark();\n\t\t$this->_tempdata_sweep();\n\t}\n}\n\n/**\n * Mock cookie driver to overload cookie setting\n */\nclass Mock_Libraries_Session_cookie extends CI_Session_cookie {\n\t/**\n\t * Overload _setcookie to manage $_COOKIE values, since actual cookies can't be set in unit testing\n\t */\n\tprotected function _setcookie($name, $value = '', $expire = 0, $path = '', $domain = '', $secure = FALSE, $httponly = FALSE)\n\t{\n\t\tif (empty($value) OR $expire <= time())\n\t\t{\n\t\t\tunset($_COOKIE[$name]);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$_COOKIE[$name] = $value;\n\t\t}\n\t}\n}\n\nclass Mock_Libraries_Session_native extends CI_Session_native {}"
  },
  {
    "path": "tests/mocks/libraries/table.php",
    "content": "<?php\n\nclass Mock_Libraries_Table extends CI_Table {\n\n\t// Override inaccessible protected method\n\tpublic function __call($method, $params)\n\t{\n\t\tif (is_callable(array($this, '_'.$method)))\n\t\t{\n\t\t\treturn call_user_func_array(array($this, '_'.$method), $params);\n\t\t}\n\n\t\tthrow new BadMethodCallException('Method '.$method.' was not found');\n\t}\n\n}\n"
  },
  {
    "path": "tests/phpunit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<phpunit\n\tbootstrap=\"./Bootstrap.php\"\n\tcolors=\"true\"\n\tconvertNoticesToExceptions=\"true\"\n\tconvertWarningsToExceptions=\"true\"\n\tstopOnError=\"false\"\n\tstopOnFailure=\"false\"\n\tstopOnIncomplete=\"false\"\n\tstopOnSkipped=\"false\"\n\tbeStrictAboutTestsThatDoNotTestAnything=\"false\">\n\t<testsuites>\n\t\t<testsuite name=\"CodeIgniter Core Test Suite\">\n\t\t\t<directory suffix=\"test.php\">./codeigniter/core</directory>\n\t\t\t<directory suffix=\"test.php\">./codeigniter/helpers</directory>\n\t\t\t<directory suffix=\"test.php\">./codeigniter/libraries</directory>\n\t\t</testsuite>\n\t</testsuites>\n\t<filter>\n\t\t<whitelist>\n\t\t\t<directory suffix=\".php\">../system/</directory>\n\t\t</whitelist>\n\t</filter>\n</phpunit>\n"
  },
  {
    "path": "tests/travis/mysql.phpunit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<phpunit \n\tbootstrap=\"../Bootstrap.php\"\n\tcolors=\"true\"\n\tconvertNoticesToExceptions=\"true\"\n\tconvertWarningsToExceptions=\"true\"\n\tstopOnError=\"false\"\n\tstopOnFailure=\"false\"\n\tstopOnIncomplete=\"false\"\n\tstopOnSkipped=\"false\"\n\tbeStrictAboutTestsThatDoNotTestAnything=\"false\">\n\t<php>\n        <const name=\"DB_DRIVER\" value=\"mysql\"/>\n    </php>\n\t<testsuites>\n\t\t<testsuite name=\"CodeIgniter Core Test Suite\">\n\t\t\t<directory suffix=\"test.php\">../codeigniter</directory>\n\t\t</testsuite>\n\t</testsuites>\n\t<filter>\n        <whitelist addUncoveredFilesFromWhitelist=\"false\">\n            <directory suffix=\".php\">../../system</directory>\n        </whitelist>\n\t</filter>\n</phpunit>"
  },
  {
    "path": "tests/travis/mysqli.phpunit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<phpunit \n\tbootstrap=\"../Bootstrap.php\"\n\tcolors=\"true\"\n\tconvertNoticesToExceptions=\"true\"\n\tconvertWarningsToExceptions=\"true\"\n\tstopOnError=\"false\"\n\tstopOnFailure=\"false\"\n\tstopOnIncomplete=\"false\"\n\tstopOnSkipped=\"false\"\n\tbeStrictAboutTestsThatDoNotTestAnything=\"false\">\n\t<php>\n        <const name=\"DB_DRIVER\" value=\"mysqli\"/>\n    </php>\n\t<testsuites>\n\t\t<testsuite name=\"CodeIgniter Core Test Suite\">\n\t\t\t<directory suffix=\"test.php\">../codeigniter</directory>\n\t\t</testsuite>\n\t</testsuites>\n\t<filter>\n        <whitelist addUncoveredFilesFromWhitelist=\"false\">\n            <directory suffix=\".php\">../../system</directory>\n        </whitelist>\n\t</filter>\n</phpunit>"
  },
  {
    "path": "tests/travis/pdo/mysql.phpunit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<phpunit \n\tbootstrap=\"../../Bootstrap.php\"\n\tcolors=\"true\"\n\tconvertNoticesToExceptions=\"true\"\n\tconvertWarningsToExceptions=\"true\"\n\tstopOnError=\"false\"\n\tstopOnFailure=\"false\"\n\tstopOnIncomplete=\"false\"\n\tstopOnSkipped=\"false\"\n\tbeStrictAboutTestsThatDoNotTestAnything=\"false\">\n\t<php>\n        <const name=\"DB_DRIVER\" value=\"pdo/mysql\"/>\n    </php>\n\t<testsuites>\n\t\t<testsuite name=\"CodeIgniter Core Test Suite\">\n\t\t\t<directory suffix=\"test.php\">../../codeigniter</directory>\n\t\t</testsuite>\n\t</testsuites>\n\t<filter>\n        <whitelist addUncoveredFilesFromWhitelist=\"false\">\n            <directory suffix=\".php\">../../../system</directory>\n        </whitelist>\n\t</filter>\n</phpunit>"
  },
  {
    "path": "tests/travis/pdo/pgsql.phpunit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<phpunit \n\tbootstrap=\"../../Bootstrap.php\"\n\tcolors=\"true\"\n\tconvertNoticesToExceptions=\"true\"\n\tconvertWarningsToExceptions=\"true\"\n\tstopOnError=\"false\"\n\tstopOnFailure=\"false\"\n\tstopOnIncomplete=\"false\"\n\tstopOnSkipped=\"false\"\n\tbeStrictAboutTestsThatDoNotTestAnything=\"false\">\n\t<php>\n        <const name=\"DB_DRIVER\" value=\"pdo/pgsql\"/>\n    </php>\n\t<testsuites>\n\t\t<testsuite name=\"CodeIgniter Core Test Suite\">\n\t\t\t<directory suffix=\"test.php\">../../codeigniter</directory>\n\t\t</testsuite>\n\t</testsuites>\n\t<filter>\n        <whitelist addUncoveredFilesFromWhitelist=\"false\">\n            <directory suffix=\".php\">../../../system</directory>\n        </whitelist>\n\t</filter>\n</phpunit>"
  },
  {
    "path": "tests/travis/pdo/sqlite.phpunit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<phpunit \n\tbootstrap=\"../../Bootstrap.php\"\n\tcolors=\"true\"\n\tconvertNoticesToExceptions=\"true\"\n\tconvertWarningsToExceptions=\"true\"\n\tstopOnError=\"false\"\n\tstopOnFailure=\"false\"\n\tstopOnIncomplete=\"false\"\n\tstopOnSkipped=\"false\"\n\tbeStrictAboutTestsThatDoNotTestAnything=\"false\">\n\t<php>\n        <const name=\"DB_DRIVER\" value=\"pdo/sqlite\"/>\n    </php>\n\t<testsuites>\n\t\t<testsuite name=\"CodeIgniter Core Test Suite\">\n\t\t\t<directory suffix=\"test.php\">../../codeigniter</directory>\n\t\t</testsuite>\n\t</testsuites>\n\t<filter>\n        <whitelist addUncoveredFilesFromWhitelist=\"false\">\n            <directory suffix=\".php\">../../../system</directory>\n        </whitelist>\n\t</filter>\n</phpunit>"
  },
  {
    "path": "tests/travis/pgsql.phpunit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<phpunit \n\tbootstrap=\"../Bootstrap.php\"\n\tcolors=\"true\"\n\tconvertNoticesToExceptions=\"true\"\n\tconvertWarningsToExceptions=\"true\"\n\tstopOnError=\"false\"\n\tstopOnFailure=\"false\"\n\tstopOnIncomplete=\"false\"\n\tstopOnSkipped=\"false\"\n\tbeStrictAboutTestsThatDoNotTestAnything=\"false\">\n\t<php>\n        <const name=\"DB_DRIVER\" value=\"pgsql\"/>\n    </php>\n\t<testsuites>\n\t\t<testsuite name=\"CodeIgniter Core Test Suite\">\n\t\t\t<directory suffix=\"test.php\">../codeigniter</directory>\n\t\t</testsuite>\n\t</testsuites>\n\t<filter>\n        <whitelist addUncoveredFilesFromWhitelist=\"false\">\n            <directory suffix=\".php\">../../system</directory>\n        </whitelist>\n\t</filter>\n</phpunit>"
  },
  {
    "path": "tests/travis/sqlite.phpunit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\tbootstrap=\"../Bootstrap.php\"\n\tcolors=\"true\"\n\tconvertNoticesToExceptions=\"true\"\n\tconvertWarningsToExceptions=\"true\"\n\tstopOnError=\"false\"\n\tstopOnFailure=\"false\"\n\tstopOnIncomplete=\"false\"\n\tstopOnSkipped=\"false\"\n\tbeStrictAboutTestsThatDoNotTestAnything=\"false\"\n\txsi:noNamespaceSchemaLocation=\"https://schema.phpunit.de/9.3/phpunit.xsd\"\n>\n  <coverage includeUncoveredFiles=\"false\">\n    <include>\n      <directory suffix=\".php\">../../system</directory>\n    </include>\n  </coverage>\n  <php>\n    <const name=\"DB_DRIVER\" value=\"sqlite\"/>\n  </php>\n  <testsuites>\n    <testsuite name=\"CodeIgniter Core Test Suite\">\n      <directory suffix=\"test.php\">../codeigniter</directory>\n    </testsuite>\n  </testsuites>\n</phpunit>\n"
  },
  {
    "path": "user_guide_src/Makefile",
    "content": "# Makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD   = sphinx-build\nPAPER         =\nBUILDDIR      = build\n\n# Internal variables.\nPAPEROPT_a4     = -D latex_paper_size=a4\nPAPEROPT_letter = -D latex_paper_size=letter\nALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source\n\n.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest\n\nhelp:\n\t@echo \"Please use \\`make <target>' where <target> is one of\"\n\t@echo \"  html       to make standalone HTML files\"\n\t@echo \"  dirhtml    to make HTML files named index.html in directories\"\n\t@echo \"  singlehtml to make a single large HTML file\"\n\t@echo \"  pickle     to make pickle files\"\n\t@echo \"  json       to make JSON files\"\n\t@echo \"  htmlhelp   to make HTML files and a HTML help project\"\n\t@echo \"  qthelp     to make HTML files and a qthelp project\"\n\t@echo \"  devhelp    to make HTML files and a Devhelp project\"\n\t@echo \"  epub       to make an epub\"\n\t@echo \"  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter\"\n\t@echo \"  latexpdf   to make LaTeX files and run them through pdflatex\"\n\t@echo \"  text       to make text files\"\n\t@echo \"  man        to make manual pages\"\n\t@echo \"  changes    to make an overview of all changed/added/deprecated items\"\n\t@echo \"  linkcheck  to check all external links for integrity\"\n\t@echo \"  doctest    to run all doctests embedded in the documentation (if enabled)\"\n\nclean:\n\t-rm -rf $(BUILDDIR)/*\n\nhtml:\n\t$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/html.\"\n\ndirhtml:\n\t$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/dirhtml.\"\n\nsinglehtml:\n\t$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml\n\t@echo\n\t@echo \"Build finished. The HTML page is in $(BUILDDIR)/singlehtml.\"\n\npickle:\n\t$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle\n\t@echo\n\t@echo \"Build finished; now you can process the pickle files.\"\n\njson:\n\t$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json\n\t@echo\n\t@echo \"Build finished; now you can process the JSON files.\"\n\nhtmlhelp:\n\t$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp\n\t@echo\n\t@echo \"Build finished; now you can run HTML Help Workshop with the\" \\\n\t      \".hhp project file in $(BUILDDIR)/htmlhelp.\"\n\nqthelp:\n\t$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp\n\t@echo\n\t@echo \"Build finished; now you can run \"qcollectiongenerator\" with the\" \\\n\t      \".qhcp project file in $(BUILDDIR)/qthelp, like this:\"\n\t@echo \"# qcollectiongenerator $(BUILDDIR)/qthelp/CodeIgniter.qhcp\"\n\t@echo \"To view the help file:\"\n\t@echo \"# assistant -collectionFile $(BUILDDIR)/qthelp/CodeIgniter.qhc\"\n\ndevhelp:\n\t$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp\n\t@echo\n\t@echo \"Build finished.\"\n\t@echo \"To view the help file:\"\n\t@echo \"# mkdir -p $$HOME/.local/share/devhelp/CodeIgniter\"\n\t@echo \"# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/CodeIgniter\"\n\t@echo \"# devhelp\"\n\nepub:\n\t$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub\n\t@echo\n\t@echo \"Build finished. The epub file is in $(BUILDDIR)/epub.\"\n\nlatex:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo\n\t@echo \"Build finished; the LaTeX files are in $(BUILDDIR)/latex.\"\n\t@echo \"Run \\`make' in that directory to run these through (pdf)latex\" \\\n\t      \"(use \\`make latexpdf' here to do that automatically).\"\n\nlatexpdf:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo \"Running LaTeX files through pdflatex...\"\n\tmake -C $(BUILDDIR)/latex all-pdf\n\t@echo \"pdflatex finished; the PDF files are in $(BUILDDIR)/latex.\"\n\ntext:\n\t$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text\n\t@echo\n\t@echo \"Build finished. The text files are in $(BUILDDIR)/text.\"\n\nman:\n\t$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man\n\t@echo\n\t@echo \"Build finished. The manual pages are in $(BUILDDIR)/man.\"\n\nchanges:\n\t$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes\n\t@echo\n\t@echo \"The overview file is in $(BUILDDIR)/changes.\"\n\nlinkcheck:\n\t$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck\n\t@echo\n\t@echo \"Link check complete; look for any errors in the above output \" \\\n\t      \"or in $(BUILDDIR)/linkcheck/output.txt.\"\n\ndoctest:\n\t$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest\n\t@echo \"Testing of doctests in the sources finished, look at the \" \\\n\t      \"results in $(BUILDDIR)/doctest/output.txt.\"\n"
  },
  {
    "path": "user_guide_src/README.rst",
    "content": "######################\nCodeIgniter User Guide\n######################\n\n******************\nSetup Instructions\n******************\n\nThe CodeIgniter user guide uses Sphinx to manage the documentation and\noutput it to various formats.  Pages are written in human-readable\n`ReStructured Text <http://sphinx.pocoo.org/rest.html>`_ format.\n\nPrerequisites\n=============\n\nSphinx requires Python 2.7.  If you are on OS X, then you already have Python.\nYou can confirm in a Terminal window by executing the ``python`` command\nwithout any parameters.  It should load up and tell you which version you have\ninstalled.\n\nNote: If you're not on Python 2.7, then you must upgrade. E.g. Install 2.7.2\nfrom https://python.org/download/releases/2.7.2/\n\nInstallation\n============\n\n1. Install `easy_install <http://peak.telecommunity.com/DevCenter/EasyInstall#installing-easy-install>`_\n2. ``easy_install \"sphinx==1.6.3\"``\n3. ``easy_install \"sphinxcontrib-phpdomain==0.1.3.post1\"``\n4. Install the CI Lexer which allows PHP, HTML, CSS, and JavaScript syntax highlighting in code examples (see *cilexer/README*)\n5. ``cd user_guide_src``\n6. ``make html``\n\nEditing and Creating Documentation\n==================================\n\nAll of the source files exist under *source/* and is where you will add new\ndocumentation or modify existing documentation.  Just as with code changes,\nwe recommend working from feature branches and making pull requests to\nthe *develop* branch of this repo.\n\nSo where's the HTML?\n====================\n\nObviously, the HTML documentation is what we care most about, as it is the\nprimary documentation that our users encounter.  Since revisions to the built\nfiles are not of value, they are not under source control.  This also allows\nyou to regenerate as necessary if you want to \"preview\" your work.  Generating\nthe HTML is very simple.  From the root directory of your user guide repo\nfork issue the command you used at the end of the installation instructions::\n\n\tmake html\n\nYou will see it do a whiz-bang compilation, at which point the fully rendered\nuser guide and images will be in *build/html/*.  After the HTML has been built,\neach successive build will only rebuild files that have changed, saving\nconsiderable time.  If for any reason you want to \"reset\" your build files,\nsimply delete the *build* folder's contents and rebuild.\n\n***************\nStyle Guideline\n***************\n\nPlease refer to source/documentation/index.rst for general guidelines for\nusing Sphinx to document CodeIgniter.\n"
  },
  {
    "path": "user_guide_src/cilexer/README",
    "content": "To install the CodeIgniter Lexer to Pygments, run:\n\n\tsudo python setup.py install\n\nConfirm with\n\n\tpygmentize -L\n\t\n\nYou should see in the lexer output:\n\n* ci, codeigniter:\n    CodeIgniter (filenames *.html, *.css, *.php, *.xml, *.static)\n\nYou will need to run setup.py and install the cilexer package anytime after cilexer/cilexer.py is updated\n\nNOTE: Depending on how you installed Sphinx and Pygments,\nyou may be installing to the wrong version. \nIf you need to install to a different version of python\nspecify its path when using setup.py, e.g.:\n\n\tsudo /usr/bin/python2.5 setup.py install"
  },
  {
    "path": "user_guide_src/cilexer/cilexer/__init__.py",
    "content": "\n"
  },
  {
    "path": "user_guide_src/cilexer/cilexer/cilexer.py",
    "content": "# CodeIgniter\n# https://codeigniter.com\n#\n# An open source application development framework for PHP\n#\n# This content is released under the MIT License (MIT)\n#\n# Copyright (c) 2014 - 2017, British Columbia Institute of Technology\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in\n# all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n# THE SOFTWARE.\n#\n# Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)\n# Copyright (c) 2014 - 2016, British Columbia Institute of Technology (https://bcit.ca/)\n#\n# https://opensource.org/licenses/MIT\tMIT License\n\nimport re\nimport copy\n\nfrom pygments.lexer import DelegatingLexer\nfrom pygments.lexers.web import PhpLexer, HtmlLexer\n\n__all__ = ['CodeIgniterLexer']\n\n\nclass CodeIgniterLexer(DelegatingLexer):\n    \"\"\"\n    Handles HTML, PHP, JavaScript, and CSS is highlighted\n    PHP is highlighted with the \"startline\" option\n    \"\"\"\n\n    name = 'CodeIgniter'\n    aliases = ['ci', 'codeigniter']\n    filenames = ['*.html', '*.css', '*.php', '*.xml', '*.static']\n    mimetypes = ['text/html', 'application/xhtml+xml']\n\n    def __init__(self, **options):\n        super(CodeIgniterLexer, self).__init__(HtmlLexer,\n                                               PhpLexer,\n                                               startinline=True)\n"
  },
  {
    "path": "user_guide_src/cilexer/setup.py",
    "content": "\"\"\"\nInstall and setup CodeIgniter highlighting for Pygments.\n\"\"\"\n\nfrom setuptools import setup\n\nentry_points = \"\"\"\n[pygments.lexers]\ncilexer = cilexer.cilexer:CodeIgniterLexer\n\"\"\"\n\nsetup(\n    name='pycilexer',\n    version='0.1',\n    description=__doc__,\n    author=\"EllisLab, Inc.\",\n    packages=['cilexer'],\n    install_requires=(\n        'sphinx >= 1.0.7',\n        'sphinxcontrib-phpdomain >= 0.1.3-1'\n    ),\n    entry_points=entry_points\n)\n"
  },
  {
    "path": "user_guide_src/source/DCO.rst",
    "content": "#####################################\nDeveloper's Certificate of Origin 1.1\n#####################################\n\nBy making a contribution to this project, I certify that:\n\n(1)\tThe contribution was created in whole or in part by me and I\n\thave the right to submit it under the open source license\n\tindicated in the file; or\n\n(2)\tThe contribution is based upon previous work that, to the best\n\tof my knowledge, is covered under an appropriate open source\n\tlicense and I have the right under that license to submit that\n\twork with modifications, whether created in whole or in part\n\tby me, under the same open source license (unless I am\n\tpermitted to submit under a different license), as indicated\n\tin the file; or\n\n(3)\tThe contribution was provided directly to me by some other\n\tperson who certified (1), (2) or (3) and I have not modified\n\tit.\n\n(4)\tI understand and agree that this project and the contribution\n\tare public and that a record of the contribution (including all\n\tpersonal information I submit with it, including my sign-off) is\n\tmaintained indefinitely and may be redistributed consistent with\n\tthis project or the open source license(s) involved.\n"
  },
  {
    "path": "user_guide_src/source/_themes/sphinx_rtd_theme/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2013 Dave Snider\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "user_guide_src/source/_themes/sphinx_rtd_theme/__init__.py",
    "content": "\"\"\"Sphinx ReadTheDocs theme.\n\nFrom https://github.com/ryan-roemer/sphinx-bootstrap-theme.\n\n\"\"\"\nimport os\n\nVERSION = (0, 1, 5)\n\n__version__ = \".\".join(str(v) for v in VERSION)\n__version_full__ = __version__\n\n\ndef get_html_theme_path():\n    \"\"\"Return list of HTML theme paths.\"\"\"\n    cur_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))\n    return cur_dir\n"
  },
  {
    "path": "user_guide_src/source/_themes/sphinx_rtd_theme/breadcrumbs.html",
    "content": "<div role=\"navigation\" aria-label=\"breadcrumbs navigation\">\n  <ul class=\"wy-breadcrumbs\">\n    <li><a href=\"{{ pathto(master_doc) }}\">Docs</a> &raquo;</li>\n      {% for doc in parents %}\n        <li><a href=\"{{ doc.link|e }}\">{{ doc.title }}</a> &raquo;</li>\n      {% endfor %}\n    <li>{{ title }}</li>\n    <li class=\"wy-breadcrumbs-aside\">\n      {% if display_github %}\n        <a href=\"https://github.com/{{ github_user }}/{{ github_repo }}/blob/{{ github_version }}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}\" class=\"fa fa-github\"> Edit on GitHub</a>\n      {% elif display_bitbucket %}\n        <a href=\"https://bitbucket.org/{{ bitbucket_user }}/{{ bitbucket_repo }}/src/{{ bitbucket_version}}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}\" class=\"fa fa-bitbucket\"> Edit on Bitbucket</a>\n      {% elif show_source and has_source and sourcename %}\n        <a href=\"{{ pathto('_sources/' + sourcename, true)|e }}\" rel=\"nofollow\"> View page source</a>\n      {% endif %}\n    </li>\n    <div style=\"float:right;margin-left:5px;\" id=\"closeMe\">\n      <img title=\"Classic Layout\" alt=\"classic layout\" src=\"data:image/gif;base64,R0lGODlhFAAUAJEAAAAAADMzM////wAAACH5BAUUAAIALAAAAAAUABQAAAImlI+py+0PU5gRBRDM3DxbWoXis42X13USOLauUIqnlsaH/eY6UwAAOw==\" />\n    </div>\n  </ul>\n  <hr/>\n</div>\n"
  },
  {
    "path": "user_guide_src/source/_themes/sphinx_rtd_theme/footer.html",
    "content": "<footer>\n  {% if next or prev %}\n    <div class=\"rst-footer-buttons\" role=\"navigation\" aria-label=\"footer navigation\">\n      {% if next %}\n        <a href=\"{{ next.link|e }}\" class=\"btn btn-neutral float-right\" title=\"{{ next.title|striptags|e }}\">Next <span class=\"fa fa-arrow-circle-right\"></span></a>\n      {% endif %}\n      {% if prev %}\n        <a href=\"{{ prev.link|e }}\" class=\"btn btn-neutral\" title=\"{{ prev.title|striptags|e }}\"><span class=\"fa fa-arrow-circle-left\"></span> Previous</a>\n      {% endif %}\n    </div>\n  {% endif %}\n\n  <hr/>\n\n  <div role=\"contentinfo\">\n    <p>\n    {%- if show_copyright %}\n      {%- if hasdoc('copyright') %}\n        {% trans path=pathto('copyright'), copyright=copyright|e %}&copy; <a href=\"{{ path }}\">Copyright</a> {{ copyright }}.{% endtrans %}\n      {%- else %}\n        {% trans copyright=copyright|e %}&copy; Copyright {{ copyright }}.{% endtrans %}\n      {%- endif %}\n    {%- endif %}\n\n    {%- if last_updated %}\n      {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %}\n    {%- endif %}\n    </p>\n  </div>\n\n  {% trans %}Built with <a href=\"http://sphinx-doc.org/\">Sphinx</a> using a <a href=\"https://github.com/snide/sphinx_rtd_theme\">theme</a> provided by <a href=\"https://readthedocs.org\">Read the Docs</a>{% endtrans %}.\n  \n</footer>\n"
  },
  {
    "path": "user_guide_src/source/_themes/sphinx_rtd_theme/layout.html",
    "content": "{# TEMPLATE VAR SETTINGS #}\n{%- set url_root = pathto('', 1) %}\n{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}\n{%- if not embedded and docstitle %}\n  {%- set titlesuffix = \" &mdash; \"|safe + docstitle|e %}\n{%- else %}\n  {%- set titlesuffix = \"\" %}\n{%- endif %}\n\n<!DOCTYPE html>\n<!--[if IE 8]><html class=\"no-js lt-ie9\" lang=\"en\" > <![endif]-->\n<!--[if gt IE 8]><!--> <html class=\"no-js\" lang=\"en\" > <!--<![endif]-->\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  {% block htmltitle %}\n  <title>{{ title|striptags|e }}{{ titlesuffix }}</title>\n  {% endblock %}\n\n  {# FAVICON #}\n  {% if favicon %}\n    <link rel=\"shortcut icon\" href=\"{{ pathto('_static/' + favicon, 1) }}\"/>\n  {% endif %}\n\n  {# CSS #}\n  <link href='https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic|Roboto+Slab:400,700|Inconsolata:400,700&subset=latin,cyrillic' rel='stylesheet' type='text/css'>\n\n  {# OPENSEARCH #}\n  {% if not embedded %}\n    {% if use_opensearch %}\n      <link rel=\"search\" type=\"application/opensearchdescription+xml\" title=\"{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}\" href=\"{{ pathto('_static/opensearch.xml', 1) }}\"/>\n    {% endif %}\n\n  {% endif %}\n\n  {# RTD hosts this file, so just load on non RTD builds #}\n  {% if not READTHEDOCS %}\n    <link rel=\"stylesheet\" href=\"{{ pathto('_static/' + style, 1) }}\" type=\"text/css\" />\n  {% endif %}\n\n  {% for cssfile in css_files %}\n    <link rel=\"stylesheet\" href=\"{{ pathto(cssfile, 1) }}\" type=\"text/css\" />\n  {% endfor %}\n\n  {%- block linktags %}\n    {%- if hasdoc('about') %}\n        <link rel=\"author\" title=\"{{ _('About these documents') }}\"\n              href=\"{{ pathto('about') }}\"/>\n    {%- endif %}\n    {%- if hasdoc('genindex') %}\n        <link rel=\"index\" title=\"{{ _('Index') }}\"\n              href=\"{{ pathto('genindex') }}\"/>\n    {%- endif %}\n    {%- if hasdoc('search') %}\n        <link rel=\"search\" title=\"{{ _('Search') }}\" href=\"{{ pathto('search') }}\"/>\n    {%- endif %}\n    {%- if hasdoc('copyright') %}\n        <link rel=\"copyright\" title=\"{{ _('Copyright') }}\" href=\"{{ pathto('copyright') }}\"/>\n    {%- endif %}\n    <link rel=\"top\" title=\"{{ docstitle|e }}\" href=\"{{ pathto('index') }}\"/>\n    {%- if parents %}\n        <link rel=\"up\" title=\"{{ parents[-1].title|striptags|e }}\" href=\"{{ parents[-1].link|e }}\"/>\n    {%- endif %}\n    {%- if next %}\n        <link rel=\"next\" title=\"{{ next.title|striptags|e }}\" href=\"{{ next.link|e }}\"/>\n    {%- endif %}\n    {%- if prev %}\n        <link rel=\"prev\" title=\"{{ prev.title|striptags|e }}\" href=\"{{ prev.link|e }}\"/>\n    {%- endif %}\n  {%- endblock %}\n  {%- block extrahead %} {% endblock %}\n\n  {# Keep modernizr in head - http://modernizr.com/docs/#installing #}\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/modernizr/2.6.2/modernizr.min.js\" integrity=\"sha384-vEQs6vKzb8v6+GpGDCnXUQ6aa2DYtn5LTi/tA/85iEZfXN0nAYj0shvYo8ldQQ7m\" crossorigin=\"anonymous\"></script>\n\n</head>\n\n<body class=\"wy-body-for-nav\" role=\"document\">\n\n  {% include \"pulldown.html\" %}\n\n  <div class=\"wy-grid-for-nav\">\n\n    {# SIDE NAV, TOGGLES ON MOBILE #}\n    <nav data-toggle=\"wy-nav-shift\" class=\"wy-nav-side\">\n      <div class=\"wy-side-nav-search\">\n        {% block sidebartitle %}\n          <a href=\"{{ pathto(master_doc) }}\" class=\"fa fa-home\"> {{ project }}</a>\n        {% endblock %}\n        {% include \"searchbox.html\" %}\n      </div>\n\n      <div class=\"wy-menu wy-menu-vertical\" data-spy=\"affix\" role=\"navigation\" aria-label=\"main navigation\">\n        {% block menu %}\n          {% set toctree = toctree(maxdepth=2, collapse=False, includehidden=False) %}\n          {% if toctree %}\n              {{ toctree }}\n          {% else %}\n              <!-- Local TOC -->\n              <div class=\"local-toc\">{{ toc }}</div>\n          {% endif %}\n        {% endblock %}\n      </div>\n      &nbsp;\n    </nav>\n\n    <section data-toggle=\"wy-nav-shift\" class=\"wy-nav-content-wrap\">\n\n      {# MOBILE NAV, TRIGGLES SIDE NAV ON TOGGLE #}\n      <nav class=\"wy-nav-top\" role=\"navigation\" aria-label=\"top navigation\">\n        <i data-toggle=\"wy-nav-top\" class=\"fa fa-bars\"></i>\n        <a href=\"{{ pathto(master_doc) }}\">{{ project }}</a>\n      </nav>\n\n\n      {# PAGE CONTENT #}\n      <div class=\"wy-nav-content\">\n        <div class=\"rst-content\">\n          {% include \"breadcrumbs.html\" %}\n          <div role=\"main\" class=\"document\">\n            {% block body %}{% endblock %}\n          </div>\n          {% include \"footer.html\" %}\n        </div>\n      </div>\n\n    </section>\n\n  </div>\n  {% include \"versions.html\" %}\n\n  {% if not embedded %}\n\n    <script type=\"text/javascript\">\n        var DOCUMENTATION_OPTIONS = {\n            URL_ROOT:'{{ url_root }}',\n            VERSION:'{{ release|e }}',\n            COLLAPSE_INDEX:false,\n            FILE_SUFFIX:'{{ '' if no_search_suffix else file_suffix }}',\n            HAS_SOURCE:  {{ has_source|lower }}\n        };\n    </script>\n    {%- for scriptfile in script_files %}\n      <script type=\"text/javascript\" src=\"{{ pathto(scriptfile, 1) }}\"></script>\n    {%- endfor %}\n\n  {% endif %}\n\n  {# RTD hosts this file, so just load on non RTD builds #}\n  {% if not READTHEDOCS %}\n    <script type=\"text/javascript\" src=\"{{ pathto('_static/js/theme.js', 1) }}\"></script>\n  {% endif %}\n\n  {# STICKY NAVIGATION #}\n  {% if theme_sticky_navigation %}\n  <script type=\"text/javascript\">\n      jQuery(function () {\n          SphinxRtdTheme.StickyNav.enable();\n      });\n  </script>\n  {% endif %}\n\n  {%- block footer %} {% endblock %}\n\n</body>\n</html>\n"
  },
  {
    "path": "user_guide_src/source/_themes/sphinx_rtd_theme/layout_old.html",
    "content": "{#\n    basic/layout.html\n    ~~~~~~~~~~~~~~~~~\n\n    Master layout template for Sphinx themes.\n\n    :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.\n    :license: BSD, see LICENSE for details.\n#}\n{%- block doctype -%}\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n  \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n{%- endblock %}\n{%- set reldelim1 = reldelim1 is not defined and ' &raquo;' or reldelim1 %}\n{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %}\n{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and\n                         (sidebars != []) %}\n{%- set url_root = pathto('', 1) %}\n{# XXX necessary? #}\n{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}\n{%- if not embedded and docstitle %}\n  {%- set titlesuffix = \" &mdash; \"|safe + docstitle|e %}\n{%- else %}\n  {%- set titlesuffix = \"\" %}\n{%- endif %}\n\n{%- macro relbar() %}\n    <div class=\"related\">\n      <h3>{{ _('Navigation') }}</h3>\n      <ul>\n        {%- for rellink in rellinks %}\n        <li class=\"right\" {% if loop.first %}style=\"margin-right: 10px\"{% endif %}>\n          <a href=\"{{ pathto(rellink[0]) }}\" title=\"{{ rellink[1]|striptags|e }}\"\n             {{ accesskey(rellink[2]) }}>{{ rellink[3] }}</a>\n          {%- if not loop.first %}{{ reldelim2 }}{% endif %}</li>\n        {%- endfor %}\n        {%- block rootrellink %}\n        <li><a href=\"{{ pathto(master_doc) }}\">{{ shorttitle|e }}</a>{{ reldelim1 }}</li>\n        {%- endblock %}\n        {%- for parent in parents %}\n          <li><a href=\"{{ parent.link|e }}\" {% if loop.last %}{{ accesskey(\"U\") }}{% endif %}>{{ parent.title }}</a>{{ reldelim1 }}</li>\n        {%- endfor %}\n        {%- block relbaritems %} {% endblock %}\n      </ul>\n    </div>\n{%- endmacro %}\n\n{%- macro sidebar() %}\n      {%- if render_sidebar %}\n      <div class=\"sphinxsidebar\">\n        <div class=\"sphinxsidebarwrapper\">\n          {%- block sidebarlogo %}\n          {%- if logo %}\n            <p class=\"logo\"><a href=\"{{ pathto(master_doc) }}\">\n              <img class=\"logo\" src=\"{{ pathto('_static/' + logo, 1) }}\" alt=\"Logo\"/>\n            </a></p>\n          {%- endif %}\n          {%- endblock %}\n          {%- if sidebars != None %}\n            {#- new style sidebar: explicitly include/exclude templates #}\n            {%- for sidebartemplate in sidebars %}\n            {%- include sidebartemplate %}\n            {%- endfor %}\n          {%- else %}\n            {#- old style sidebars: using blocks -- should be deprecated #}\n            {%- block sidebartoc %}\n            {%- include \"localtoc.html\" %}\n            {%- endblock %}\n            {%- block sidebarrel %}\n            {%- include \"relations.html\" %}\n            {%- endblock %}\n            {%- block sidebarsourcelink %}\n            {%- include \"sourcelink.html\" %}\n            {%- endblock %}\n            {%- if customsidebar %}\n            {%- include customsidebar %}\n            {%- endif %}\n            {%- block sidebarsearch %}\n            {%- include \"searchbox.html\" %}\n            {%- endblock %}\n          {%- endif %}\n        </div>\n      </div>\n      {%- endif %}\n{%- endmacro %}\n\n{%- macro script() %}\n    <script type=\"text/javascript\">\n      var DOCUMENTATION_OPTIONS = {\n        URL_ROOT:    '{{ url_root }}',\n        VERSION:     '{{ release|e }}',\n        COLLAPSE_INDEX: false,\n        FILE_SUFFIX: '{{ '' if no_search_suffix else file_suffix }}',\n        HAS_SOURCE:  {{ has_source|lower }}\n      };\n    </script>\n    {%- for scriptfile in script_files %}\n    <script type=\"text/javascript\" src=\"{{ pathto(scriptfile, 1) }}\"></script>\n    {%- endfor %}\n{%- endmacro %}\n\n{%- macro css() %}\n    <link rel=\"stylesheet\" href=\"{{ pathto('_static/' + style, 1) }}\" type=\"text/css\" />\n    <link rel=\"stylesheet\" href=\"{{ pathto('_static/pygments.css', 1) }}\" type=\"text/css\" />\n    {%- for cssfile in css_files %}\n    <link rel=\"stylesheet\" href=\"{{ pathto(cssfile, 1) }}\" type=\"text/css\" />\n    {%- endfor %}\n{%- endmacro %}\n\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n  <head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset={{ encoding }}\" />\n    {{ metatags }}\n    {%- block htmltitle %}\n    <title>{{ title|striptags|e }}{{ titlesuffix }}</title>\n    {%- endblock %}\n    {{ css() }}\n    {%- if not embedded %}\n    {{ script() }}\n    {%- if use_opensearch %}\n    <link rel=\"search\" type=\"application/opensearchdescription+xml\"\n          title=\"{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}\"\n          href=\"{{ pathto('_static/opensearch.xml', 1) }}\"/>\n    {%- endif %}\n    {%- if favicon %}\n    <link rel=\"shortcut icon\" href=\"{{ pathto('_static/' + favicon, 1) }}\"/>\n    {%- endif %}\n    {%- endif %}\n{%- block linktags %}\n    {%- if hasdoc('about') %}\n    <link rel=\"author\" title=\"{{ _('About these documents') }}\" href=\"{{ pathto('about') }}\" />\n    {%- endif %}\n    {%- if hasdoc('genindex') %}\n    <link rel=\"index\" title=\"{{ _('Index') }}\" href=\"{{ pathto('genindex') }}\" />\n    {%- endif %}\n    {%- if hasdoc('search') %}\n    <link rel=\"search\" title=\"{{ _('Search') }}\" href=\"{{ pathto('search') }}\" />\n    {%- endif %}\n    {%- if hasdoc('copyright') %}\n    <link rel=\"copyright\" title=\"{{ _('Copyright') }}\" href=\"{{ pathto('copyright') }}\" />\n    {%- endif %}\n    <link rel=\"top\" title=\"{{ docstitle|e }}\" href=\"{{ pathto('index') }}\" />\n    {%- if parents %}\n    <link rel=\"up\" title=\"{{ parents[-1].title|striptags|e }}\" href=\"{{ parents[-1].link|e }}\" />\n    {%- endif %}\n    {%- if next %}\n    <link rel=\"next\" title=\"{{ next.title|striptags|e }}\" href=\"{{ next.link|e }}\" />\n    {%- endif %}\n    {%- if prev %}\n    <link rel=\"prev\" title=\"{{ prev.title|striptags|e }}\" href=\"{{ prev.link|e }}\" />\n    {%- endif %}\n{%- endblock %}\n{%- block extrahead %} {% endblock %}\n  </head>\n  <body>\n{%- block header %}{% endblock %}\n\n{%- block relbar1 %}{{ relbar() }}{% endblock %}\n\n{%- block content %}\n  {%- block sidebar1 %} {# possible location for sidebar #} {% endblock %}\n\n    <div class=\"document\">\n  {%- block document %}\n      <div class=\"documentwrapper\">\n      {%- if render_sidebar %}\n        <div class=\"bodywrapper\">\n      {%- endif %}\n          <div class=\"body\">\n            {% block body %} {% endblock %}\n          </div>\n      {%- if render_sidebar %}\n        </div>\n      {%- endif %}\n      </div>\n  {%- endblock %}\n\n  {%- block sidebar2 %}{{ sidebar() }}{% endblock %}\n      <div class=\"clearer\"></div>\n    </div>\n{%- endblock %}\n\n{%- block relbar2 %}{{ relbar() }}{% endblock %}\n\n{%- block footer %}\n    <div class=\"footer\">\n    {%- if show_copyright %}\n      {%- if hasdoc('copyright') %}\n        {% trans path=pathto('copyright'), copyright=copyright|e %}&copy; <a href=\"{{ path }}\">Copyright</a> {{ copyright }}.{% endtrans %}\n      {%- else %}\n        {% trans copyright=copyright|e %}&copy; Copyright {{ copyright }}.{% endtrans %}\n      {%- endif %}\n    {%- endif %}\n    {%- if last_updated %}\n      {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %}\n    {%- endif %}\n    {%- if show_sphinx %}\n      {% trans sphinx_version=sphinx_version|e %}Created using <a href=\"http://sphinx-doc.org/\">Sphinx</a> {{ sphinx_version }}.{% endtrans %}\n    {%- endif %}\n    </div>\n    <p>asdf asdf asdf asdf 22</p>\n{%- endblock %}\n  </body>\n</html>\n\n"
  },
  {
    "path": "user_guide_src/source/_themes/sphinx_rtd_theme/pulldown.html",
    "content": "<div id=\"nav\">\n  <div id=\"nav_inner\">\n    {% block ciNav %}\n    {% set toctree = toctree(maxdepth=2, collapse=False, includehidden=False) %}\n    {% if toctree %}\n      <div id=\"pulldown-menu\" class=\"ciNav\">\n        {{ toctree }}\n      </div>\n    {% endif %}\n      {% endblock %}\n  </div>\n</div>\n<div id=\"nav2\">\n  <a href=\"#\" id=\"openToc\">\n    <img src=\"data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAARgAA/+4ADkFkb2JlAGTAAAAAAf/bAIQABAMDAwMDBAMDBAYEAwQGBwUEBAUHCAYGBwYGCAoICQkJCQgKCgwMDAwMCgwMDQ0MDBERERERFBQUFBQUFBQUFAEEBQUIBwgPCgoPFA4ODhQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU/8AAEQgAKwCaAwERAAIRAQMRAf/EAHsAAQAABwEBAAAAAAAAAAAAAAABAwQFBgcIAgkBAQAAAAAAAAAAAAAAAAAAAAAQAAEDAwICBwYEAgsAAAAAAAIBAwQAEQUSBiEHkROTVNQWGDFBUVIUCHEiMtOUFWGBobHRQlMkZIRVEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwDSC+ygkOOaUoKigUCgUCgUCgUCgUCgUCgUCgkuGguIP9FBMFb0Hqg7We+3jlmIqqYFf4ub+/QYlnOR/LqIBKGFUbf8qWv971BytQXXE7Y3Lnm3HsFhp2TaZJAdchRXpIgSpdEJWxJEW3xoKV7F5OMy7JkQn2o7D6w33XGjEAkoiqrJEqIiOIiKuhePCgqp22dyYyS3CyWHnQ5joG61HkRnmnTbaFSMhExRVQRRVJU9iUHjE7ez+fJ0MFipmUNhBV8YUd2SoIV9KkjQla9ltegttBdPLW4/qocL+UTfrMiHW4+P9M71shuyrqaHTcxsl7jegpsji8nh5ZwMvDfgTm0RTjSmjYdFCS6KoOIipdFunCgmNYTMv457MMY6U7iI6oMieDDhRm1VbIhuoOkbqtuK0Hpzb+eZcYZexUxt6UyUqK2cd0SdjtgrhOgijcgERUlJOCIl6CpgbP3blRI8XgMjNARAyKNDfeRBdFDBVUAXgQrqH4pxoJTu2NysY97LP4ac1io5q1InHFeGO24LnVKJuKOkSQ/yKir+rh7aCLG1dzypZQI2FnvTgccYOM3FeN0XWERXAUEFVQgQkUktdLpegm+Td3/Xli/L+S/mYNJIOF9G/wBeLKrZHFb0akG6W1WtQWSg3Dyg5e7V3fipE3O4/wCrktyzYA+ufas2LbZIlmnAT2kvuoN1wft95augilglX/tzP3qCu9O3LL/wV/i5v79BvmTADq14UGu91467Z6U9y0HzH/ncj/U/sT/CgynZG7I2NezpZGUjIycJkYkZSG+uQ81pbBNKLxJfjwoMqZ3/ALYHl35AJ7/cuwHcu5k7r1Q5pHetBjquqVVJWGxj9Zrtcl/Ggy3dHMvauR3HFZj5nHNxSyW5JISYDMoIwx8tFIGHZhPNaykGapr6rUAiicEoMG21lMRj8buPAz8xhJrr7uOeiPTCyAwXUaGR1mgozbTusOsFLEiJ7fbQa/h7gcjy2H3V6xppwDNtUSxCJIqp7valBuWVzJ22xuCROXNNZiJkMtms0DbjUkAZjzoDrTMd9dDRI44ZC2YsrYdKWP2WDT2S3N9dNdlRYrGMYc06IURXSYb0igrpWS485xVNS6nF4rwslkoMwnbpgZLB7bmt5uMweAhDEl4B5uSLzzqTnnyVpW2jaJHRMSIjdDiiotvy3DOE5rYTEbkl5yFn28k7JyG4c7AU2HtLH1uKfaiMPI40CdYbpNtmLdwTSn5rewLNld+7TLdeal4WarWBkbVKBjgdElMJJwAAY5fl4kB3b1fp4XvagsGS3FjJfLzDNtS8aeXx7LzT7TyzByQE5PccRGRC0ZRUDRV6y62vbjagzLmJzS2vuPK43JY6aP1TW6Jz+RIWyFtyC06y3EkiiinAo7YCqfq1AqqnGgsOH3lhZO8d1pmcpB8j5XIm9OYlBJSQ/FSS4427DKO0RC8AlcEMhFdViRR1WDWR5t3WXVuL1d106kG9vdeye2g60+1FDyW0shIcXVpyroXt8I8dfd+NB1vioAdWnD3UF1+gD4UFc6CEKpagxXN43rwJLUHz7yX2c8zokt9uHlsPIhA4aRnnHJTLptIS6CNsY7iASpxUUMkReGpfbQW0vtN5pitvrsN28rwtBD0nc0+/Yft5XhaB6TuaXfsP28rwtA9J3NPv2H7eV4Wgek7mn37D9vK8LQPSdzT79h+3leFoHpO5pd+w/byvC0D0nc0u/Yft5XhaB6TuaXfsP28rwtA9J3NLv2H7eV4Wgek7ml37D9vK8LQPSdzS79h+3leFoHpO5p9+w/byvC0E9r7Reazy2HIYVPxkS/CUHVn26cosxyv2g7h89LYmZSXOenvLEQ1YaQ222RATcQCP8rSGqqA8S02W2pQ6FhMoAIlqCtsnwoCpdKClejI4i3Sgtb+GBxVuNBSFt1pV/RQefLjPyUDy4z8lA8uM/JQPLjPyUDy4z8lA8uM/JQPLjPyUDy4z8lA8uM/JQPLjPyUDy4z8lA8utJ/koJ7WCbBU/LQXOPAFq1koK8B0pag90CggtBBf6qB0UDooHRQOigdFA6KB0UDooHRQOigdFA6KB0UDooI0EaBQf//Z\" title=\"Toggle Table of Contents\" alt=\"Toggle Table of Contents\" />\n  </a>\n</div>\n"
  },
  {
    "path": "user_guide_src/source/_themes/sphinx_rtd_theme/search.html",
    "content": "{#\n    basic/search.html\n    ~~~~~~~~~~~~~~~~~\n\n    Template for the search page.\n\n    :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.\n    :license: BSD, see LICENSE for details.\n#}\n{%- extends \"layout.html\" %}\n{% set title = _('Search') %}\n{% set script_files = script_files + ['_static/searchtools.js'] %}\n{% block footer %}\n  <script type=\"text/javascript\">\n    jQuery(function() { Search.loadIndex(\"{{ pathto('searchindex.js', 1) }}\"); });\n  </script>\n  {# this is used when loading the search index using $.ajax fails,\n     such as on Chrome for documents on localhost #}\n  <script type=\"text/javascript\" id=\"searchindexloader\"></script>\n  {{ super() }}\n{% endblock %}\n{% block body %}\n  <noscript>\n  <div id=\"fallback\" class=\"admonition warning\">\n    <p class=\"last\">\n      {% trans %}Please activate JavaScript to enable the search\n      functionality.{% endtrans %}\n    </p>\n  </div>\n  </noscript>\n\n  {% if search_performed %}\n    <h2>{{ _('Search Results') }}</h2>\n    {% if not search_results %}\n      <p>{{ _('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\\'ve selected enough categories.') }}</p>\n    {% endif %}\n  {% endif %}\n  <div id=\"search-results\">\n  {% if search_results %}\n    <ul>\n    {% for href, caption, context in search_results %}\n      <li>\n        <a href=\"{{ pathto(item.href) }}\">{{ caption }}</a>\n        <p class=\"context\">{{ context|e }}</p>\n      </li>\n    {% endfor %}\n    </ul>\n  {% endif %}\n  </div>\n{% endblock %}\n"
  },
  {
    "path": "user_guide_src/source/_themes/sphinx_rtd_theme/searchbox.html",
    "content": "{%- if builder != 'singlehtml' %}\n<div role=\"search\">\n  <form id=\"rtd-search-form\" class=\"wy-form\" action=\"{{ pathto('search') }}\" method=\"get\">\n    <input type=\"text\" name=\"q\" placeholder=\"Search docs\" />\n    <input type=\"hidden\" name=\"check_keywords\" value=\"yes\" />\n    <input type=\"hidden\" name=\"area\" value=\"default\" />\n  </form>\n</div>\n{%- endif %}\n"
  },
  {
    "path": "user_guide_src/source/_themes/sphinx_rtd_theme/static/css/badge_only.css",
    "content": "﻿.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:\"\"}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url(\"../font/fontawesome_webfont.eot\");src:url(\"../font/fontawesome_webfont.eot?#iefix\") format(\"embedded-opentype\"),url(\"../font/fontawesome_webfont.woff\") format(\"woff\"),url(\"../font/fontawesome_webfont.ttf\") format(\"truetype\"),url(\"../font/fontawesome_webfont.svg#FontAwesome\") format(\"svg\")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:\"\"}.icon-book:before{content:\"\"}.fa-caret-down:before{content:\"\"}.icon-caret-down:before{content:\"\"}.fa-caret-up:before{content:\"\"}.icon-caret-up:before{content:\"\"}.fa-caret-left:before{content:\"\"}.icon-caret-left:before{content:\"\"}.fa-caret-right:before{content:\"\"}.icon-caret-right:before{content:\"\"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:\"Lato\",\"proxima-nova\",\"Helvetica Neue\",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:\"\"}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}\n/*# sourceMappingURL=badge_only.css.map */\n"
  },
  {
    "path": "user_guide_src/source/_themes/sphinx_rtd_theme/static/css/citheme.css",
    "content": "@import 'theme.css';\n\n.highlighted {\n\tpadding: 0px !important;\n\tfont-weight: inherit !important;\n\tbackground-color: #f1d40f !important;\n}\n\n#nav {\n\tbackground-color: #494949;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: none;\n}\n\n#nav2 {\n\tbackground: url(data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAARgAA/+4ADkFkb2JlAGTAAAAAAf/bAIQABAMDAwMDBAMDBAYEAwQGBwUEBAUHCAYGBwYGCAoICQkJCQgKCgwMDAwMCgwMDQ0MDBERERERFBQUFBQUFBQUFAEEBQUIBwgPCgoPFA4ODhQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU/8AAEQgAMgAzAwERAAIRAQMRAf/EAFkAAQADAQAAAAAAAAAAAAAAAAABBQcIAQEAAAAAAAAAAAAAAAAAAAAAEAABAgYDAAAAAAAAAAAAAAAAAVERAtMEFJRVBxgRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AMRAAAAAAAA7a87dZcCu3e1wHnbrLgV272uA87dZcCu3e1wHnbrLgV272uA87dZcCu3e1wHnbrLgV272uA87dZcCu3e1wN/wJGAYEjAMCRgGBIwDAkYBgSMAwJGAsoIwCCMAgjAIIwCCMAgjAIIwEgAAAAAAAAAAAAAAAAAAAAAAAH//2Q==) repeat-x scroll left top transparent;\n\tmargin: 0;\n\tpadding: 0 310px 0 0;\n\ttext-align: right;\n\tdisplay: none;\n}\n\n#nav_inner {\n\tbackground-color: transparent;\n\tfont-family: Lucida Grande,Verdana,Geneva,sans-serif;\n\tfont-size: 11px;\n\tmargin: 0;\n\tpadding: 8px 12px 0 20px;\n}\n\ndiv#pulldown-menu {\n\t-moz-column-count: 5;\n\t-moz-column-gap: 20px;\n\t-webkit-column-count: 5;\n\t-webkit-column-gap: 20px;\n\tcolumn-count: 5;\n\tcolumn-gap: 20px;\n\t-webkit-column-rule: 1px groove #b8b8b8;\n\t-moz-column-rule: 1px groove #b8b8b8;\n\tcolumn-rule: 1px groove #b8b8b8;\n}\n\n#pulldown-menu > ul {\n\tpadding-top: 10px;\n\tpadding-bottom: 10px;\n\t-webkit-column-break-inside: avoid; /*Chrome, Safari*/\n\tdisplay: table;\t/*Firefox*/\n\tbreak-inside: avoid; /*IE 10+ theoretically*/\n}\n\n#pulldown-menu ul li.toctree-l2 {\n\tfont-size: 0.82em;\n\tmargin-left: 20px;\n\tlist-style-image: url(data:image/gif;base64,R0lGODlhCwAJALMJAO7u7uTk5PLy8unp6fb29t7e3vj4+Li4uIWFheTk5AAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAkALAAAAAALAAkAAAQoMJ1JqTQ4Z3SI98jHCWSJkByArCyiHkMsIzEX3DeCc0Xv+4hEa5iIAAA7);\n}\n\n#pulldown-menu ul li.toctree-l1 a {\n\tcolor: #ffffff;\n\ttext-decoration: none;\n\tfont-size: 12px;\n\tfont-family: \"Roboto Slab\",\"ff-tisa-web-pro\",\"Georgia\",Arial,sans-serif;\n\tfont-weight: 700;\n}\n\n#pulldown-menu ul li.toctree-l2 a {\n\ttext-decoration: none;\n\tfont-size: 11px;\n\tline-height: 1.4em;\n\tfont-weight: 300;\n\tfont-family: Lucida Grande,Verdana,Geneva,sans-serif;\n\tcolor: #aaaaaa;\n}\n\n/*hide pulldown menu on mobile devices*/\n@media (max-width: 768px) { /*tablet size defined by theme*/\n\t#closeMe {\n\t\tdisplay: none;\n\t}\n\n\t#pulldown {\n\t\tdisplay: none;\n\t}\n\n\t#openToc {\n\t\tdisplay: none;\n\t}\n}"
  },
  {
    "path": "user_guide_src/source/_themes/sphinx_rtd_theme/static/css/theme.css",
    "content": "*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}[hidden]{display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:hover,a:active{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;color:#000;text-decoration:none}mark{background:#ff0;color:#000;font-style:italic;font-weight:bold}pre,code,.rst-content tt,kbd,samp{font-family:monospace,serif;_font-family:\"courier new\",monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:before,q:after{content:\"\";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}ul,ol,dl{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:0;margin:0;padding:0}label{cursor:pointer}legend{border:0;*margin-left:-7px;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type=\"button\"],input[type=\"reset\"],input[type=\"submit\"]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type=\"checkbox\"],input[type=\"radio\"]{box-sizing:border-box;padding:0;*width:13px;*height:13px}input[type=\"search\"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=\"search\"]::-webkit-search-decoration,input[type=\"search\"]::-webkit-search-cancel-button{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top;resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:0.2em 0;background:#ccc;color:#000;padding:0.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none !important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{html,body,section{background:none !important}*{box-shadow:none !important;text-shadow:none !important;filter:none !important;-ms-filter:none !important}a,a:visited{text-decoration:underline}.ir a:after,a[href^=\"javascript:\"]:after,a[href^=\"#\"]:after{content:\"\"}pre,blockquote{page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:1.5cm 0.5cm 2.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}.fa:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo,.btn,input[type=\"text\"],input[type=\"password\"],input[type=\"email\"],input[type=\"url\"],input[type=\"date\"],input[type=\"month\"],input[type=\"time\"],input[type=\"datetime\"],input[type=\"datetime-local\"],input[type=\"week\"],input[type=\"number\"],input[type=\"search\"],input[type=\"tel\"],input[type=\"color\"],select,textarea,.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a,.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a,.wy-nav-top a{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:\"\"}.clearfix:after{clear:both}/*!\n *  Font Awesome 4.1.0 by @davegandy - http://fontawesome.io - @fontawesome\n *  License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)\n */@font-face{font-family:'FontAwesome';src:url(\"../fonts/fontawesome-webfont.eot?v=4.1.0\");src:url(\"../fonts/fontawesome-webfont.eot?#iefix&v=4.1.0\") format(\"embedded-opentype\"),url(\"../fonts/fontawesome-webfont.woff?v=4.1.0\") format(\"woff\"),url(\"../fonts/fontawesome-webfont.ttf?v=4.1.0\") format(\"truetype\"),url(\"../fonts/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular\") format(\"svg\");font-weight:normal;font-style:normal}.fa,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.icon{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:0.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:0.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.rst-content .pull-left.admonition-title,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content dl dt .pull-left.headerlink,.pull-left.icon{margin-right:.3em}.fa.pull-right,.rst-content .pull-right.admonition-title,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content dl dt .pull-right.headerlink,.pull-right.icon{margin-left:.3em}.fa-spin{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@keyframes spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0);-webkit-transform:scale(-1, 1);-moz-transform:scale(-1, 1);-ms-transform:scale(-1, 1);-o-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:scale(1, -1);-moz-transform:scale(1, -1);-ms-transform:scale(1, -1);-o-transform:scale(1, -1);transform:scale(1, -1)}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:\"\"}.fa-music:before{content:\"\"}.fa-search:before,.icon-search:before{content:\"\"}.fa-envelope-o:before{content:\"\"}.fa-heart:before{content:\"\"}.fa-star:before{content:\"\"}.fa-star-o:before{content:\"\"}.fa-user:before{content:\"\"}.fa-film:before{content:\"\"}.fa-th-large:before{content:\"\"}.fa-th:before{content:\"\"}.fa-th-list:before{content:\"\"}.fa-check:before{content:\"\"}.fa-times:before{content:\"\"}.fa-search-plus:before{content:\"\"}.fa-search-minus:before{content:\"\"}.fa-power-off:before{content:\"\"}.fa-signal:before{content:\"\"}.fa-gear:before,.fa-cog:before{content:\"\"}.fa-trash-o:before{content:\"\"}.fa-home:before,.icon-home:before{content:\"\"}.fa-file-o:before{content:\"\"}.fa-clock-o:before{content:\"\"}.fa-road:before{content:\"\"}.fa-download:before{content:\"\"}.fa-arrow-circle-o-down:before{content:\"\"}.fa-arrow-circle-o-up:before{content:\"\"}.fa-inbox:before{content:\"\"}.fa-play-circle-o:before{content:\"\"}.fa-rotate-right:before,.fa-repeat:before{content:\"\"}.fa-refresh:before{content:\"\"}.fa-list-alt:before{content:\"\"}.fa-lock:before{content:\"\"}.fa-flag:before{content:\"\"}.fa-headphones:before{content:\"\"}.fa-volume-off:before{content:\"\"}.fa-volume-down:before{content:\"\"}.fa-volume-up:before{content:\"\"}.fa-qrcode:before{content:\"\"}.fa-barcode:before{content:\"\"}.fa-tag:before{content:\"\"}.fa-tags:before{content:\"\"}.fa-book:before,.icon-book:before{content:\"\"}.fa-bookmark:before{content:\"\"}.fa-print:before{content:\"\"}.fa-camera:before{content:\"\"}.fa-font:before{content:\"\"}.fa-bold:before{content:\"\"}.fa-italic:before{content:\"\"}.fa-text-height:before{content:\"\"}.fa-text-width:before{content:\"\"}.fa-align-left:before{content:\"\"}.fa-align-center:before{content:\"\"}.fa-align-right:before{content:\"\"}.fa-align-justify:before{content:\"\"}.fa-list:before{content:\"\"}.fa-dedent:before,.fa-outdent:before{content:\"\"}.fa-indent:before{content:\"\"}.fa-video-camera:before{content:\"\"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:\"\"}.fa-pencil:before{content:\"\"}.fa-map-marker:before{content:\"\"}.fa-adjust:before{content:\"\"}.fa-tint:before{content:\"\"}.fa-edit:before,.fa-pencil-square-o:before{content:\"\"}.fa-share-square-o:before{content:\"\"}.fa-check-square-o:before{content:\"\"}.fa-arrows:before{content:\"\"}.fa-step-backward:before{content:\"\"}.fa-fast-backward:before{content:\"\"}.fa-backward:before{content:\"\"}.fa-play:before{content:\"\"}.fa-pause:before{content:\"\"}.fa-stop:before{content:\"\"}.fa-forward:before{content:\"\"}.fa-fast-forward:before{content:\"\"}.fa-step-forward:before{content:\"\"}.fa-eject:before{content:\"\"}.fa-chevron-left:before{content:\"\"}.fa-chevron-right:before{content:\"\"}.fa-plus-circle:before{content:\"\"}.fa-minus-circle:before{content:\"\"}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:\"\"}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:\"\"}.fa-question-circle:before{content:\"\"}.fa-info-circle:before{content:\"\"}.fa-crosshairs:before{content:\"\"}.fa-times-circle-o:before{content:\"\"}.fa-check-circle-o:before{content:\"\"}.fa-ban:before{content:\"\"}.fa-arrow-left:before{content:\"\"}.fa-arrow-right:before{content:\"\"}.fa-arrow-up:before{content:\"\"}.fa-arrow-down:before{content:\"\"}.fa-mail-forward:before,.fa-share:before{content:\"\"}.fa-expand:before{content:\"\"}.fa-compress:before{content:\"\"}.fa-plus:before{content:\"\"}.fa-minus:before{content:\"\"}.fa-asterisk:before{content:\"\"}.fa-exclamation-circle:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.rst-content .admonition-title:before{content:\"\"}.fa-gift:before{content:\"\"}.fa-leaf:before{content:\"\"}.fa-fire:before,.icon-fire:before{content:\"\"}.fa-eye:before{content:\"\"}.fa-eye-slash:before{content:\"\"}.fa-warning:before,.fa-exclamation-triangle:before{content:\"\"}.fa-plane:before{content:\"\"}.fa-calendar:before{content:\"\"}.fa-random:before{content:\"\"}.fa-comment:before{content:\"\"}.fa-magnet:before{content:\"\"}.fa-chevron-up:before{content:\"\"}.fa-chevron-down:before{content:\"\"}.fa-retweet:before{content:\"\"}.fa-shopping-cart:before{content:\"\"}.fa-folder:before{content:\"\"}.fa-folder-open:before{content:\"\"}.fa-arrows-v:before{content:\"\"}.fa-arrows-h:before{content:\"\"}.fa-bar-chart-o:before{content:\"\"}.fa-twitter-square:before{content:\"\"}.fa-facebook-square:before{content:\"\"}.fa-camera-retro:before{content:\"\"}.fa-key:before{content:\"\"}.fa-gears:before,.fa-cogs:before{content:\"\"}.fa-comments:before{content:\"\"}.fa-thumbs-o-up:before{content:\"\"}.fa-thumbs-o-down:before{content:\"\"}.fa-star-half:before{content:\"\"}.fa-heart-o:before{content:\"\"}.fa-sign-out:before{content:\"\"}.fa-linkedin-square:before{content:\"\"}.fa-thumb-tack:before{content:\"\"}.fa-external-link:before{content:\"\"}.fa-sign-in:before{content:\"\"}.fa-trophy:before{content:\"\"}.fa-github-square:before{content:\"\"}.fa-upload:before{content:\"\"}.fa-lemon-o:before{content:\"\"}.fa-phone:before{content:\"\"}.fa-square-o:before{content:\"\"}.fa-bookmark-o:before{content:\"\"}.fa-phone-square:before{content:\"\"}.fa-twitter:before{content:\"\"}.fa-facebook:before{content:\"\"}.fa-github:before,.icon-github:before{content:\"\"}.fa-unlock:before{content:\"\"}.fa-credit-card:before{content:\"\"}.fa-rss:before{content:\"\"}.fa-hdd-o:before{content:\"\"}.fa-bullhorn:before{content:\"\"}.fa-bell:before{content:\"\"}.fa-certificate:before{content:\"\"}.fa-hand-o-right:before{content:\"\"}.fa-hand-o-left:before{content:\"\"}.fa-hand-o-up:before{content:\"\"}.fa-hand-o-down:before{content:\"\"}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:\"\"}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:\"\"}.fa-arrow-circle-up:before{content:\"\"}.fa-arrow-circle-down:before{content:\"\"}.fa-globe:before{content:\"\"}.fa-wrench:before{content:\"\"}.fa-tasks:before{content:\"\"}.fa-filter:before{content:\"\"}.fa-briefcase:before{content:\"\"}.fa-arrows-alt:before{content:\"\"}.fa-group:before,.fa-users:before{content:\"\"}.fa-chain:before,.fa-link:before,.icon-link:before{content:\"\"}.fa-cloud:before{content:\"\"}.fa-flask:before{content:\"\"}.fa-cut:before,.fa-scissors:before{content:\"\"}.fa-copy:before,.fa-files-o:before{content:\"\"}.fa-paperclip:before{content:\"\"}.fa-save:before,.fa-floppy-o:before{content:\"\"}.fa-square:before{content:\"\"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:\"\"}.fa-list-ul:before{content:\"\"}.fa-list-ol:before{content:\"\"}.fa-strikethrough:before{content:\"\"}.fa-underline:before{content:\"\"}.fa-table:before{content:\"\"}.fa-magic:before{content:\"\"}.fa-truck:before{content:\"\"}.fa-pinterest:before{content:\"\"}.fa-pinterest-square:before{content:\"\"}.fa-google-plus-square:before{content:\"\"}.fa-google-plus:before{content:\"\"}.fa-money:before{content:\"\"}.fa-caret-down:before,.wy-dropdown .caret:before,.icon-caret-down:before{content:\"\"}.fa-caret-up:before{content:\"\"}.fa-caret-left:before{content:\"\"}.fa-caret-right:before{content:\"\"}.fa-columns:before{content:\"\"}.fa-unsorted:before,.fa-sort:before{content:\"\"}.fa-sort-down:before,.fa-sort-desc:before{content:\"\"}.fa-sort-up:before,.fa-sort-asc:before{content:\"\"}.fa-envelope:before{content:\"\"}.fa-linkedin:before{content:\"\"}.fa-rotate-left:before,.fa-undo:before{content:\"\"}.fa-legal:before,.fa-gavel:before{content:\"\"}.fa-dashboard:before,.fa-tachometer:before{content:\"\"}.fa-comment-o:before{content:\"\"}.fa-comments-o:before{content:\"\"}.fa-flash:before,.fa-bolt:before{content:\"\"}.fa-sitemap:before{content:\"\"}.fa-umbrella:before{content:\"\"}.fa-paste:before,.fa-clipboard:before{content:\"\"}.fa-lightbulb-o:before{content:\"\"}.fa-exchange:before{content:\"\"}.fa-cloud-download:before{content:\"\"}.fa-cloud-upload:before{content:\"\"}.fa-user-md:before{content:\"\"}.fa-stethoscope:before{content:\"\"}.fa-suitcase:before{content:\"\"}.fa-bell-o:before{content:\"\"}.fa-coffee:before{content:\"\"}.fa-cutlery:before{content:\"\"}.fa-file-text-o:before{content:\"\"}.fa-building-o:before{content:\"\"}.fa-hospital-o:before{content:\"\"}.fa-ambulance:before{content:\"\"}.fa-medkit:before{content:\"\"}.fa-fighter-jet:before{content:\"\"}.fa-beer:before{content:\"\"}.fa-h-square:before{content:\"\"}.fa-plus-square:before{content:\"\"}.fa-angle-double-left:before{content:\"\"}.fa-angle-double-right:before{content:\"\"}.fa-angle-double-up:before{content:\"\"}.fa-angle-double-down:before{content:\"\"}.fa-angle-left:before{content:\"\"}.fa-angle-right:before{content:\"\"}.fa-angle-up:before{content:\"\"}.fa-angle-down:before{content:\"\"}.fa-desktop:before{content:\"\"}.fa-laptop:before{content:\"\"}.fa-tablet:before{content:\"\"}.fa-mobile-phone:before,.fa-mobile:before{content:\"\"}.fa-circle-o:before{content:\"\"}.fa-quote-left:before{content:\"\"}.fa-quote-right:before{content:\"\"}.fa-spinner:before{content:\"\"}.fa-circle:before{content:\"\"}.fa-mail-reply:before,.fa-reply:before{content:\"\"}.fa-github-alt:before{content:\"\"}.fa-folder-o:before{content:\"\"}.fa-folder-open-o:before{content:\"\"}.fa-smile-o:before{content:\"\"}.fa-frown-o:before{content:\"\"}.fa-meh-o:before{content:\"\"}.fa-gamepad:before{content:\"\"}.fa-keyboard-o:before{content:\"\"}.fa-flag-o:before{content:\"\"}.fa-flag-checkered:before{content:\"\"}.fa-terminal:before{content:\"\"}.fa-code:before{content:\"\"}.fa-mail-reply-all:before,.fa-reply-all:before{content:\"\"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:\"\"}.fa-location-arrow:before{content:\"\"}.fa-crop:before{content:\"\"}.fa-code-fork:before{content:\"\"}.fa-unlink:before,.fa-chain-broken:before{content:\"\"}.fa-question:before{content:\"\"}.fa-info:before{content:\"\"}.fa-exclamation:before{content:\"\"}.fa-superscript:before{content:\"\"}.fa-subscript:before{content:\"\"}.fa-eraser:before{content:\"\"}.fa-puzzle-piece:before{content:\"\"}.fa-microphone:before{content:\"\"}.fa-microphone-slash:before{content:\"\"}.fa-shield:before{content:\"\"}.fa-calendar-o:before{content:\"\"}.fa-fire-extinguisher:before{content:\"\"}.fa-rocket:before{content:\"\"}.fa-maxcdn:before{content:\"\"}.fa-chevron-circle-left:before{content:\"\"}.fa-chevron-circle-right:before{content:\"\"}.fa-chevron-circle-up:before{content:\"\"}.fa-chevron-circle-down:before{content:\"\"}.fa-html5:before{content:\"\"}.fa-css3:before{content:\"\"}.fa-anchor:before{content:\"\"}.fa-unlock-alt:before{content:\"\"}.fa-bullseye:before{content:\"\"}.fa-ellipsis-h:before{content:\"\"}.fa-ellipsis-v:before{content:\"\"}.fa-rss-square:before{content:\"\"}.fa-play-circle:before{content:\"\"}.fa-ticket:before{content:\"\"}.fa-minus-square:before{content:\"\"}.fa-minus-square-o:before{content:\"\"}.fa-level-up:before{content:\"\"}.fa-level-down:before{content:\"\"}.fa-check-square:before{content:\"\"}.fa-pencil-square:before{content:\"\"}.fa-external-link-square:before{content:\"\"}.fa-share-square:before{content:\"\"}.fa-compass:before{content:\"\"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:\"\"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:\"\"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:\"\"}.fa-euro:before,.fa-eur:before{content:\"\"}.fa-gbp:before{content:\"\"}.fa-dollar:before,.fa-usd:before{content:\"\"}.fa-rupee:before,.fa-inr:before{content:\"\"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:\"\"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:\"\"}.fa-won:before,.fa-krw:before{content:\"\"}.fa-bitcoin:before,.fa-btc:before{content:\"\"}.fa-file:before{content:\"\"}.fa-file-text:before{content:\"\"}.fa-sort-alpha-asc:before{content:\"\"}.fa-sort-alpha-desc:before{content:\"\"}.fa-sort-amount-asc:before{content:\"\"}.fa-sort-amount-desc:before{content:\"\"}.fa-sort-numeric-asc:before{content:\"\"}.fa-sort-numeric-desc:before{content:\"\"}.fa-thumbs-up:before{content:\"\"}.fa-thumbs-down:before{content:\"\"}.fa-youtube-square:before{content:\"\"}.fa-youtube:before{content:\"\"}.fa-xing:before{content:\"\"}.fa-xing-square:before{content:\"\"}.fa-youtube-play:before{content:\"\"}.fa-dropbox:before{content:\"\"}.fa-stack-overflow:before{content:\"\"}.fa-instagram:before{content:\"\"}.fa-flickr:before{content:\"\"}.fa-adn:before{content:\"\"}.fa-bitbucket:before,.icon-bitbucket:before{content:\"\"}.fa-bitbucket-square:before{content:\"\"}.fa-tumblr:before{content:\"\"}.fa-tumblr-square:before{content:\"\"}.fa-long-arrow-down:before{content:\"\"}.fa-long-arrow-up:before{content:\"\"}.fa-long-arrow-left:before{content:\"\"}.fa-long-arrow-right:before{content:\"\"}.fa-apple:before{content:\"\"}.fa-windows:before{content:\"\"}.fa-android:before{content:\"\"}.fa-linux:before{content:\"\"}.fa-dribbble:before{content:\"\"}.fa-skype:before{content:\"\"}.fa-foursquare:before{content:\"\"}.fa-trello:before{content:\"\"}.fa-female:before{content:\"\"}.fa-male:before{content:\"\"}.fa-gittip:before{content:\"\"}.fa-sun-o:before{content:\"\"}.fa-moon-o:before{content:\"\"}.fa-archive:before{content:\"\"}.fa-bug:before{content:\"\"}.fa-vk:before{content:\"\"}.fa-weibo:before{content:\"\"}.fa-renren:before{content:\"\"}.fa-pagelines:before{content:\"\"}.fa-stack-exchange:before{content:\"\"}.fa-arrow-circle-o-right:before{content:\"\"}.fa-arrow-circle-o-left:before{content:\"\"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:\"\"}.fa-dot-circle-o:before{content:\"\"}.fa-wheelchair:before{content:\"\"}.fa-vimeo-square:before{content:\"\"}.fa-turkish-lira:before,.fa-try:before{content:\"\"}.fa-plus-square-o:before{content:\"\"}.fa-space-shuttle:before{content:\"\"}.fa-slack:before{content:\"\"}.fa-envelope-square:before{content:\"\"}.fa-wordpress:before{content:\"\"}.fa-openid:before{content:\"\"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:\"\"}.fa-mortar-board:before,.fa-graduation-cap:before{content:\"\"}.fa-yahoo:before{content:\"\"}.fa-google:before{content:\"\"}.fa-reddit:before{content:\"\"}.fa-reddit-square:before{content:\"\"}.fa-stumbleupon-circle:before{content:\"\"}.fa-stumbleupon:before{content:\"\"}.fa-delicious:before{content:\"\"}.fa-digg:before{content:\"\"}.fa-pied-piper-square:before,.fa-pied-piper:before{content:\"\"}.fa-pied-piper-alt:before{content:\"\"}.fa-drupal:before{content:\"\"}.fa-joomla:before{content:\"\"}.fa-language:before{content:\"\"}.fa-fax:before{content:\"\"}.fa-building:before{content:\"\"}.fa-child:before{content:\"\"}.fa-paw:before{content:\"\"}.fa-spoon:before{content:\"\"}.fa-cube:before{content:\"\"}.fa-cubes:before{content:\"\"}.fa-behance:before{content:\"\"}.fa-behance-square:before{content:\"\"}.fa-steam:before{content:\"\"}.fa-steam-square:before{content:\"\"}.fa-recycle:before{content:\"\"}.fa-automobile:before,.fa-car:before{content:\"\"}.fa-cab:before,.fa-taxi:before{content:\"\"}.fa-tree:before{content:\"\"}.fa-spotify:before{content:\"\"}.fa-deviantart:before{content:\"\"}.fa-soundcloud:before{content:\"\"}.fa-database:before{content:\"\"}.fa-file-pdf-o:before{content:\"\"}.fa-file-word-o:before{content:\"\"}.fa-file-excel-o:before{content:\"\"}.fa-file-powerpoint-o:before{content:\"\"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:\"\"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:\"\"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:\"\"}.fa-file-movie-o:before,.fa-file-video-o:before{content:\"\"}.fa-file-code-o:before{content:\"\"}.fa-vine:before{content:\"\"}.fa-codepen:before{content:\"\"}.fa-jsfiddle:before{content:\"\"}.fa-life-bouy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:\"\"}.fa-circle-o-notch:before{content:\"\"}.fa-ra:before,.fa-rebel:before{content:\"\"}.fa-ge:before,.fa-empire:before{content:\"\"}.fa-git-square:before{content:\"\"}.fa-git:before{content:\"\"}.fa-hacker-news:before{content:\"\"}.fa-tencent-weibo:before{content:\"\"}.fa-qq:before{content:\"\"}.fa-wechat:before,.fa-weixin:before{content:\"\"}.fa-send:before,.fa-paper-plane:before{content:\"\"}.fa-send-o:before,.fa-paper-plane-o:before{content:\"\"}.fa-history:before{content:\"\"}.fa-circle-thin:before{content:\"\"}.fa-header:before{content:\"\"}.fa-paragraph:before{content:\"\"}.fa-sliders:before{content:\"\"}.fa-share-alt:before{content:\"\"}.fa-share-alt-square:before{content:\"\"}.fa-bomb:before{content:\"\"}.fa,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.icon,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context{font-family:inherit}.fa:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before{font-family:\"FontAwesome\";display:inline-block;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa,a .rst-content .admonition-title,.rst-content a .admonition-title,a .rst-content h1 .headerlink,.rst-content h1 a .headerlink,a .rst-content h2 .headerlink,.rst-content h2 a .headerlink,a .rst-content h3 .headerlink,.rst-content h3 a .headerlink,a .rst-content h4 .headerlink,.rst-content h4 a .headerlink,a .rst-content h5 .headerlink,.rst-content h5 a .headerlink,a .rst-content h6 .headerlink,.rst-content h6 a .headerlink,a .rst-content dl dt .headerlink,.rst-content dl dt a .headerlink,a .icon{display:inline-block;text-decoration:inherit}.btn .fa,.btn .rst-content .admonition-title,.rst-content .btn .admonition-title,.btn .rst-content h1 .headerlink,.rst-content h1 .btn .headerlink,.btn .rst-content h2 .headerlink,.rst-content h2 .btn .headerlink,.btn .rst-content h3 .headerlink,.rst-content h3 .btn .headerlink,.btn .rst-content h4 .headerlink,.rst-content h4 .btn .headerlink,.btn .rst-content h5 .headerlink,.rst-content h5 .btn .headerlink,.btn .rst-content h6 .headerlink,.rst-content h6 .btn .headerlink,.btn .rst-content dl dt .headerlink,.rst-content dl dt .btn .headerlink,.btn .icon,.nav .fa,.nav .rst-content .admonition-title,.rst-content .nav .admonition-title,.nav .rst-content h1 .headerlink,.rst-content h1 .nav .headerlink,.nav .rst-content h2 .headerlink,.rst-content h2 .nav .headerlink,.nav .rst-content h3 .headerlink,.rst-content h3 .nav .headerlink,.nav .rst-content h4 .headerlink,.rst-content h4 .nav .headerlink,.nav .rst-content h5 .headerlink,.rst-content h5 .nav .headerlink,.nav .rst-content h6 .headerlink,.rst-content h6 .nav .headerlink,.nav .rst-content dl dt .headerlink,.rst-content dl dt .nav .headerlink,.nav .icon{display:inline}.btn .fa.fa-large,.btn .rst-content .fa-large.admonition-title,.rst-content .btn .fa-large.admonition-title,.btn .rst-content h1 .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.btn .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .btn .fa-large.headerlink,.btn .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .fa-large.admonition-title,.rst-content .nav .fa-large.admonition-title,.nav .rst-content h1 .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.nav .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.nav .fa-large.icon{line-height:0.9em}.btn .fa.fa-spin,.btn .rst-content .fa-spin.admonition-title,.rst-content .btn .fa-spin.admonition-title,.btn .rst-content h1 .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.btn .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .btn .fa-spin.headerlink,.btn .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .fa-spin.admonition-title,.rst-content .nav .fa-spin.admonition-title,.nav .rst-content h1 .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.nav .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.nav .fa-spin.icon{display:inline-block}.btn.fa:before,.rst-content .btn.admonition-title:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content dl dt .btn.headerlink:before,.btn.icon:before{opacity:0.5;-webkit-transition:opacity 0.05s ease-in;-moz-transition:opacity 0.05s ease-in;transition:opacity 0.05s ease-in}.btn.fa:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.btn.icon:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .rst-content .admonition-title:before,.rst-content .btn-mini .admonition-title:before,.btn-mini .rst-content h1 .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.btn-mini .rst-content dl dt .headerlink:before,.rst-content dl dt .btn-mini .headerlink:before,.btn-mini .icon:before{font-size:14px;vertical-align:-15%}.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo{padding:12px;line-height:24px;margin-bottom:24px;background:#dedede}.wy-alert-title,.rst-content .admonition-title{color:#fff;font-weight:bold;display:block;color:#fff;background:#8ba8af;margin:-12px;padding:6px 12px;margin-bottom:12px}.wy-alert.wy-alert-danger,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.admonition-todo{background:#fdf3f2}.wy-alert.wy-alert-danger .wy-alert-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .danger .wy-alert-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .danger .admonition-title,.rst-content .error .admonition-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title{background:#f29f97}.wy-alert.wy-alert-warning,.rst-content .wy-alert-warning.note,.rst-content .attention,.rst-content .caution,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.tip,.rst-content .warning,.rst-content .wy-alert-warning.seealso,.rst-content .admonition-todo{background:#ffedcc}.wy-alert.wy-alert-warning .wy-alert-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .attention .wy-alert-title,.rst-content .caution .wy-alert-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .admonition-todo .wy-alert-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .attention .admonition-title,.rst-content .caution .admonition-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .warning .admonition-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .admonition-todo .admonition-title{background:#f0b37e}.wy-alert.wy-alert-info,.rst-content .note,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.rst-content .seealso,.rst-content .wy-alert-info.admonition-todo{background:#dedede}.wy-alert.wy-alert-info .wy-alert-title,.rst-content .note .wy-alert-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.rst-content .note .admonition-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .seealso .admonition-title,.rst-content .wy-alert-info.admonition-todo .admonition-title{background:#8ba8af}.wy-alert.wy-alert-success,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.warning,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.admonition-todo{background:#dedede}.wy-alert.wy-alert-success .wy-alert-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .hint .wy-alert-title,.rst-content .important .wy-alert-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .hint .admonition-title,.rst-content .important .admonition-title,.rst-content .tip .admonition-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.admonition-todo .admonition-title{background:#dd4814}.wy-alert.wy-alert-neutral,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.admonition-todo{background:#f3f6f6}.wy-alert.wy-alert-neutral .wy-alert-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .admonition-title{color:#404040;background:#e1e4e5}.wy-alert.wy-alert-neutral a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.admonition-todo a{color:#dd4814}.wy-alert p:last-child,.rst-content .note p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.rst-content .seealso p:last-child,.rst-content .admonition-todo p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0px;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,0.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all 0.3s ease-in;-moz-transition:all 0.3s ease-in;transition:all 0.3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27AE60}.wy-tray-container li.wy-tray-item-info{background:#dd4814}.wy-tray-container li.wy-tray-item-warning{background:#E67E22}.wy-tray-container li.wy-tray-item-danger{background:#E74C3C}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width: 768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px 12px;color:#fff;border:1px solid rgba(0,0,0,0.1);background-color:#27AE60;text-decoration:none;font-weight:normal;font-family:\"Lato\",\"proxima-nova\",\"Helvetica Neue\",Arial,sans-serif;box-shadow:0px 1px 2px -1px rgba(255,255,255,0.5) inset,0px -2px 0px 0px rgba(0,0,0,0.1) inset;outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all 0.1s linear;-moz-transition:all 0.1s linear;transition:all 0.1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:0px -1px 0px 0px rgba(0,0,0,0.05) inset,0px 2px 0px 0px rgba(0,0,0,0.1) inset;padding:8px 12px 6px 12px}.btn:visited{color:#fff}.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled:hover,.btn-disabled:focus,.btn-disabled:active{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#dd4814 !important}.btn-info:hover{background-color:#2e8ece !important}.btn-neutral{background-color:#f3f6f6 !important;color:#404040 !important}.btn-neutral:hover{background-color:#e5ebeb !important;color:#404040}.btn-neutral:visited{color:#404040 !important}.btn-success{background-color:#27AE60 !important}.btn-success:hover{background-color:#295 !important}.btn-danger{background-color:#E74C3C !important}.btn-danger:hover{background-color:#ea6153 !important}.btn-warning{background-color:#E67E22 !important}.btn-warning:hover{background-color:#e98b39 !important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f !important}.btn-link{background-color:transparent !important;color:#dd4814;box-shadow:none;border-color:transparent !important}.btn-link:hover{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:active{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:visited{color:#97310e}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:before,.wy-btn-group:after{display:table;content:\"\"}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:solid 1px #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,0.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#dd4814;color:#fff}.wy-dropdown-menu>dd.divider{border-top:solid 1px #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type=\"search\"]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#dd4814;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;text-align:right}.wy-dropdown-arrow:before{content:\" \";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned input,.wy-form-aligned textarea,.wy-form-aligned select,.wy-form-aligned .wy-help-inline,.wy-form-aligned label{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{border:0;margin:0;padding:0}legend{display:block;width:100%;border:0;padding:0;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label{display:block;margin:0 0 0.3125em 0;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:before,.wy-control-group:after{display:table;content:\"\"}.wy-control-group:after{clear:both}.wy-control-group:before,.wy-control-group:after{display:table;content:\"\"}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:\" *\";color:#E74C3C}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full input[type=\"text\"],.wy-control-group .wy-form-full input[type=\"password\"],.wy-control-group .wy-form-full input[type=\"email\"],.wy-control-group .wy-form-full input[type=\"url\"],.wy-control-group .wy-form-full input[type=\"date\"],.wy-control-group .wy-form-full input[type=\"month\"],.wy-control-group .wy-form-full input[type=\"time\"],.wy-control-group .wy-form-full input[type=\"datetime\"],.wy-control-group .wy-form-full input[type=\"datetime-local\"],.wy-control-group .wy-form-full input[type=\"week\"],.wy-control-group .wy-form-full input[type=\"number\"],.wy-control-group .wy-form-full input[type=\"search\"],.wy-control-group .wy-form-full input[type=\"tel\"],.wy-control-group .wy-form-full input[type=\"color\"],.wy-control-group .wy-form-halves input[type=\"text\"],.wy-control-group .wy-form-halves input[type=\"password\"],.wy-control-group .wy-form-halves input[type=\"email\"],.wy-control-group .wy-form-halves input[type=\"url\"],.wy-control-group .wy-form-halves input[type=\"date\"],.wy-control-group .wy-form-halves input[type=\"month\"],.wy-control-group .wy-form-halves input[type=\"time\"],.wy-control-group .wy-form-halves input[type=\"datetime\"],.wy-control-group .wy-form-halves input[type=\"datetime-local\"],.wy-control-group .wy-form-halves input[type=\"week\"],.wy-control-group .wy-form-halves input[type=\"number\"],.wy-control-group .wy-form-halves input[type=\"search\"],.wy-control-group .wy-form-halves input[type=\"tel\"],.wy-control-group .wy-form-halves input[type=\"color\"],.wy-control-group .wy-form-thirds input[type=\"text\"],.wy-control-group .wy-form-thirds input[type=\"password\"],.wy-control-group .wy-form-thirds input[type=\"email\"],.wy-control-group .wy-form-thirds input[type=\"url\"],.wy-control-group .wy-form-thirds input[type=\"date\"],.wy-control-group .wy-form-thirds input[type=\"month\"],.wy-control-group .wy-form-thirds input[type=\"time\"],.wy-control-group .wy-form-thirds input[type=\"datetime\"],.wy-control-group .wy-form-thirds input[type=\"datetime-local\"],.wy-control-group .wy-form-thirds input[type=\"week\"],.wy-control-group .wy-form-thirds input[type=\"number\"],.wy-control-group .wy-form-thirds input[type=\"search\"],.wy-control-group .wy-form-thirds input[type=\"tel\"],.wy-control-group .wy-form-thirds input[type=\"color\"]{width:100%}.wy-control-group .wy-form-full{float:left;display:block;margin-right:2.35765%;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child{margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n+1){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child{margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control{margin:6px 0 0 0;font-size:90%}.wy-control-no-input{display:inline-block;margin:6px 0 0 0;font-size:90%}.wy-control-group.fluid-input input[type=\"text\"],.wy-control-group.fluid-input input[type=\"password\"],.wy-control-group.fluid-input input[type=\"email\"],.wy-control-group.fluid-input input[type=\"url\"],.wy-control-group.fluid-input input[type=\"date\"],.wy-control-group.fluid-input input[type=\"month\"],.wy-control-group.fluid-input input[type=\"time\"],.wy-control-group.fluid-input input[type=\"datetime\"],.wy-control-group.fluid-input input[type=\"datetime-local\"],.wy-control-group.fluid-input input[type=\"week\"],.wy-control-group.fluid-input input[type=\"number\"],.wy-control-group.fluid-input input[type=\"search\"],.wy-control-group.fluid-input input[type=\"tel\"],.wy-control-group.fluid-input input[type=\"color\"]{width:100%}.wy-form-message-inline{display:inline-block;padding-left:0.3em;color:#666;vertical-align:middle;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:0.3125em;font-style:italic}input{line-height:normal}input[type=\"button\"],input[type=\"reset\"],input[type=\"submit\"]{-webkit-appearance:button;cursor:pointer;font-family:\"Lato\",\"proxima-nova\",\"Helvetica Neue\",Arial,sans-serif;*overflow:visible}input[type=\"text\"],input[type=\"password\"],input[type=\"email\"],input[type=\"url\"],input[type=\"date\"],input[type=\"month\"],input[type=\"time\"],input[type=\"datetime\"],input[type=\"datetime-local\"],input[type=\"week\"],input[type=\"number\"],input[type=\"search\"],input[type=\"tel\"],input[type=\"color\"]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:\"Lato\",\"proxima-nova\",\"Helvetica Neue\",Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}input[type=\"datetime-local\"]{padding:0.34375em 0.625em}input[disabled]{cursor:default}input[type=\"checkbox\"],input[type=\"radio\"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0;margin-right:0.3125em;*height:13px;*width:13px}input[type=\"search\"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=\"search\"]::-webkit-search-cancel-button,input[type=\"search\"]::-webkit-search-decoration{-webkit-appearance:none}input[type=\"text\"]:focus,input[type=\"password\"]:focus,input[type=\"email\"]:focus,input[type=\"url\"]:focus,input[type=\"date\"]:focus,input[type=\"month\"]:focus,input[type=\"time\"]:focus,input[type=\"datetime\"]:focus,input[type=\"datetime-local\"]:focus,input[type=\"week\"]:focus,input[type=\"number\"]:focus,input[type=\"search\"]:focus,input[type=\"tel\"]:focus,input[type=\"color\"]:focus{outline:0;outline:thin dotted \\9;border-color:#333}input.no-focus:focus{border-color:#ccc !important}input[type=\"file\"]:focus,input[type=\"radio\"]:focus,input[type=\"checkbox\"]:focus{outline:thin dotted #333;outline:1px auto #129FEA}input[type=\"text\"][disabled],input[type=\"password\"][disabled],input[type=\"email\"][disabled],input[type=\"url\"][disabled],input[type=\"date\"][disabled],input[type=\"month\"][disabled],input[type=\"time\"][disabled],input[type=\"datetime\"][disabled],input[type=\"datetime-local\"][disabled],input[type=\"week\"][disabled],input[type=\"number\"][disabled],input[type=\"search\"][disabled],input[type=\"tel\"][disabled],input[type=\"color\"][disabled]{cursor:not-allowed;background-color:#f3f6f6;color:#cad2d3}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#E74C3C;border:1px solid #E74C3C}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#E74C3C}input[type=\"file\"]:focus:invalid:focus,input[type=\"radio\"]:focus:invalid:focus,input[type=\"checkbox\"]:focus:invalid:focus{outline-color:#E74C3C}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:\"Lato\",\"proxima-nova\",\"Helvetica Neue\",Arial,sans-serif}select,textarea{padding:0.5em 0.625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#fff;color:#cad2d3;border-color:transparent}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:solid 1px #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#E74C3C}.wy-control-group.wy-control-group-error input[type=\"text\"],.wy-control-group.wy-control-group-error input[type=\"password\"],.wy-control-group.wy-control-group-error input[type=\"email\"],.wy-control-group.wy-control-group-error input[type=\"url\"],.wy-control-group.wy-control-group-error input[type=\"date\"],.wy-control-group.wy-control-group-error input[type=\"month\"],.wy-control-group.wy-control-group-error input[type=\"time\"],.wy-control-group.wy-control-group-error input[type=\"datetime\"],.wy-control-group.wy-control-group-error input[type=\"datetime-local\"],.wy-control-group.wy-control-group-error input[type=\"week\"],.wy-control-group.wy-control-group-error input[type=\"number\"],.wy-control-group.wy-control-group-error input[type=\"search\"],.wy-control-group.wy-control-group-error input[type=\"tel\"],.wy-control-group.wy-control-group-error input[type=\"color\"]{border:solid 1px #E74C3C}.wy-control-group.wy-control-group-error textarea{border:solid 1px #E74C3C}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:0.5em 0.625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27AE60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#E74C3C}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#E67E22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#dd4814}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width: 480px){.wy-form button[type=\"submit\"]{margin:0.7em 0 0}.wy-form input[type=\"text\"],.wy-form input[type=\"password\"],.wy-form input[type=\"email\"],.wy-form input[type=\"url\"],.wy-form input[type=\"date\"],.wy-form input[type=\"month\"],.wy-form input[type=\"time\"],.wy-form input[type=\"datetime\"],.wy-form input[type=\"datetime-local\"],.wy-form input[type=\"week\"],.wy-form input[type=\"number\"],.wy-form input[type=\"search\"],.wy-form input[type=\"tel\"],.wy-form input[type=\"color\"]{margin-bottom:0.3em;display:block}.wy-form label{margin-bottom:0.3em;display:block}.wy-form input[type=\"password\"],.wy-form input[type=\"email\"],.wy-form input[type=\"url\"],.wy-form input[type=\"date\"],.wy-form input[type=\"month\"],.wy-form input[type=\"time\"],.wy-form input[type=\"datetime\"],.wy-form input[type=\"datetime-local\"],.wy-form input[type=\"week\"],.wy-form input[type=\"number\"],.wy-form input[type=\"search\"],.wy-form input[type=\"tel\"],.wy-form input[type=\"color\"]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:0.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0 0}.wy-form .wy-help-inline,.wy-form-message-inline,.wy-form-message{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width: 768px){.tablet-hide{display:none}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}}@media screen and (max-width: 480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.wy-table,.rst-content table.docutils,.rst-content table.field-list{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.wy-table caption,.rst-content table.docutils caption,.rst-content table.field-list caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td,.wy-table th,.rst-content table.docutils th,.rst-content table.field-list th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.wy-table td:first-child,.rst-content table.docutils td:first-child,.rst-content table.field-list td:first-child,.wy-table th:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list th:first-child{border-left-width:0}.wy-table thead,.rst-content table.docutils thead,.rst-content table.field-list thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.wy-table thead th,.rst-content table.docutils thead th,.rst-content table.field-list thead th{font-weight:bold;border-bottom:solid 2px #e1e4e5}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td{background-color:transparent;vertical-align:middle}.wy-table td p,.rst-content table.docutils td p,.rst-content table.field-list td p{line-height:18px}.wy-table td p:last-child,.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child{margin-bottom:0}.wy-table .wy-table-cell-min,.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min{width:1%;padding-right:0}.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:gray;font-size:90%}.wy-table-tertiary{color:gray;font-size:80%}.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td,.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td{background-color:#f3f6f6}.wy-table-backed{background-color:#f3f6f6}.wy-table-bordered-all,.rst-content table.docutils{border:1px solid #e1e4e5}.wy-table-bordered-all td,.rst-content table.docutils td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.wy-table-bordered-all tbody>tr:last-child td,.rst-content table.docutils tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0 !important}a{color:#dd4814;text-decoration:none;cursor:pointer}a:hover{color:#97310e}a:visited{color:#97310e}html{height:100%;overflow-x:hidden}body{font-family:\"Lato\",\"proxima-nova\",\"Helvetica Neue\",Arial,sans-serif;font-weight:normal;color:#404040;min-height:100%;overflow-x:hidden;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#E67E22 !important}a.wy-text-warning:hover{color:#eb9950 !important}.wy-text-info{color:#dd4814 !important}a.wy-text-info:hover{color:#409ad5 !important}.wy-text-success{color:#27AE60 !important}a.wy-text-success:hover{color:#36d278 !important}.wy-text-danger{color:#E74C3C !important}a.wy-text-danger:hover{color:#ed7669 !important}.wy-text-neutral{color:#404040 !important}a.wy-text-neutral:hover{color:#595959 !important}h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:\"Roboto Slab\",\"ff-tisa-web-pro\",\"Georgia\",Arial,sans-serif}p{line-height:24px;margin:0;font-size:16px;margin-bottom:24px}h1{font-size:175%}h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}code,.rst-content tt{white-space:nowrap;max-width:100%;background:#fff;border:solid 1px #e1e4e5;font-size:75%;padding:0 5px;font-family:Consolas,\"Andale Mono WT\",\"Andale Mono\",\"Lucida Console\",\"Lucida Sans Typewriter\",\"DejaVu Sans Mono\",\"Bitstream Vera Sans Mono\",\"Liberation Mono\",\"Nimbus Mono L\",Monaco,\"Courier New\",Courier,monospace;color:#E74C3C;overflow-x:auto}code.code-large,.rst-content tt.code-large{font-size:90%}.wy-plain-list-disc,.rst-content .section ul,.rst-content .toctree-wrapper ul,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.wy-plain-list-disc li,.rst-content .section ul li,.rst-content .toctree-wrapper ul li,article ul li{list-style:disc;margin-left:24px}.wy-plain-list-disc li p:last-child,.rst-content .section ul li p:last-child,.rst-content .toctree-wrapper ul li p:last-child,article ul li p:last-child{margin-bottom:0}.wy-plain-list-disc li ul,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li ul,article ul li ul{margin-bottom:0}.wy-plain-list-disc li li,.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,article ul li li{list-style:circle}.wy-plain-list-disc li li li,.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,article ul li li li{list-style:square}.wy-plain-list-disc li ol li,.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,article ul li ol li{list-style:decimal}.wy-plain-list-decimal,.rst-content .section ol,.rst-content ol.arabic,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.wy-plain-list-decimal li,.rst-content .section ol li,.rst-content ol.arabic li,article ol li{list-style:decimal;margin-left:24px}.wy-plain-list-decimal li p:last-child,.rst-content .section ol li p:last-child,.rst-content ol.arabic li p:last-child,article ol li p:last-child{margin-bottom:0}.wy-plain-list-decimal li ul,.rst-content .section ol li ul,.rst-content ol.arabic li ul,article ol li ul{margin-bottom:0}.wy-plain-list-decimal li ul li,.rst-content .section ol li ul li,.rst-content ol.arabic li ul li,article ol li ul li{list-style:disc}.codeblock-example{border:1px solid #e1e4e5;border-bottom:none;padding:24px;padding-top:48px;font-weight:500;background:#fff;position:relative}.codeblock-example:after{content:\"Example\";position:absolute;top:0px;left:0px;background:#97310e;color:#fff;padding:6px 12px}.codeblock-example.prettyprint-example-only{border:1px solid #e1e4e5;margin-bottom:24px}.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight']{border:1px solid #e1e4e5;padding:0px;overflow-x:auto;background:#fff;margin:1px 0 24px 0}.codeblock div[class^='highlight'],pre.literal-block div[class^='highlight'],.rst-content .literal-block div[class^='highlight'],div[class^='highlight'] div[class^='highlight']{border:none;background:none;margin:0}div[class^='highlight'] td.code{width:100%}.linenodiv pre{border-right:solid 1px #e6e9ea;margin:0;padding:12px 12px;font-family:Consolas,\"Andale Mono WT\",\"Andale Mono\",\"Lucida Console\",\"Lucida Sans Typewriter\",\"DejaVu Sans Mono\",\"Bitstream Vera Sans Mono\",\"Liberation Mono\",\"Nimbus Mono L\",Monaco,\"Courier New\",Courier,monospace;font-size:12px;line-height:1.5;color:#d9d9d9}div[class^='highlight'] pre{white-space:pre;margin:0;padding:12px 12px;font-family:Consolas,\"Andale Mono WT\",\"Andale Mono\",\"Lucida Console\",\"Lucida Sans Typewriter\",\"DejaVu Sans Mono\",\"Bitstream Vera Sans Mono\",\"Liberation Mono\",\"Nimbus Mono L\",Monaco,\"Courier New\",Courier,monospace;font-size:12px;line-height:1.5;display:block;overflow:auto;color:#404040}@media print{.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight'],div[class^='highlight'] pre{white-space:pre-wrap}}.hll{background-color:#ffc;margin:0 -12px;padding:0 12px;display:block}.c{color:#998;font-style:italic}.err{color:#a61717;background-color:#e3d2d2}.k{font-weight:bold}.o{font-weight:bold}.cm{color:#998;font-style:italic}.cp{color:#999;font-weight:bold}.c1{color:#998;font-style:italic}.cs{color:#999;font-weight:bold;font-style:italic}.gd{color:#000;background-color:#fdd}.gd .x{color:#000;background-color:#faa}.ge{font-style:italic}.gr{color:#a00}.gh{color:#999}.gi{color:#000;background-color:#dfd}.gi .x{color:#000;background-color:#afa}.go{color:#888}.gp{color:#555}.gs{font-weight:bold}.gu{color:purple;font-weight:bold}.gt{color:#a00}.kc{font-weight:bold}.kd{font-weight:bold}.kn{font-weight:bold}.kp{font-weight:bold}.kr{font-weight:bold}.kt{color:#458;font-weight:bold}.m{color:#099}.s{color:#d14}.n{color:#333}.na{color:teal}.nb{color:#0086b3}.nc{color:#458;font-weight:bold}.no{color:teal}.ni{color:purple}.ne{color:#900;font-weight:bold}.nf{color:#900;font-weight:bold}.nn{color:#555}.nt{color:navy}.nv{color:teal}.ow{font-weight:bold}.w{color:#bbb}.mf{color:#099}.mh{color:#099}.mi{color:#099}.mo{color:#099}.sb{color:#d14}.sc{color:#d14}.sd{color:#d14}.s2{color:#d14}.se{color:#d14}.sh{color:#d14}.si{color:#d14}.sx{color:#d14}.sr{color:#009926}.s1{color:#d14}.ss{color:#990073}.bp{color:#999}.vc{color:teal}.vg{color:teal}.vi{color:teal}.il{color:#099}.gc{color:#999;background-color:#EAF2F5}.wy-breadcrumbs li{display:inline-block}.wy-breadcrumbs li.wy-breadcrumbs-aside{float:right}.wy-breadcrumbs li a{display:inline-block;padding:5px}.wy-breadcrumbs li a:first-child{padding-left:0}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width: 480px){.wy-breadcrumbs-extra{display:none}.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:before,.wy-menu-horiz:after{display:table;content:\"\"}.wy-menu-horiz:after{clear:both}.wy-menu-horiz ul,.wy-menu-horiz li{display:inline-block}.wy-menu-horiz li:hover{background:rgba(255,255,255,0.1)}.wy-menu-horiz li.divide-left{border-left:solid 1px #404040}.wy-menu-horiz li.divide-right{border-right:solid 1px #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical header{height:32px;display:inline-block;line-height:32px;padding:0 1.618em;display:block;font-weight:bold;text-transform:uppercase;font-size:80%;color:#dd4814;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:solid 1px #404040}.wy-menu-vertical li.divide-bottom{border-bottom:solid 1px #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:gray;border-right:solid 1px #c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a{color:#404040;padding:0.4045em 1.618em;font-weight:bold;position:relative;background:#fcfcfc;border:none;border-bottom:solid 1px #c9c9c9;border-top:solid 1px #c9c9c9;padding-left:1.618em -4px}.wy-menu-vertical li.on a:hover,.wy-menu-vertical li.current>a:hover{background:#fcfcfc}.wy-menu-vertical li.toctree-l2.current>a{background:#c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical .local-toc li ul{display:block}.wy-menu-vertical li ul li a{margin-bottom:0;color:#b3b3b3;font-weight:normal}.wy-menu-vertical a{display:inline-block;line-height:18px;padding:0.4045em 1.618em;display:block;position:relative;font-size:90%;color:#b3b3b3}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:active{background-color:#dd4814;cursor:pointer;color:#fff}.wy-side-nav-search{z-index:200;background-color:#dd4814;text-align:center;padding:0.809em;display:block;color:#fcfcfc;margin-bottom:0.809em}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#97310e}.wy-side-nav-search img{display:block;margin:auto auto 0.809em auto;height:45px;width:45px;background-color:#dd4814;padding:5px;border-radius:100%}.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a{color:#fcfcfc;font-size:100%;font-weight:bold;display:inline-block;padding:4px 6px;margin-bottom:0.809em}.wy-side-nav-search>a:hover,.wy-side-nav-search .wy-dropdown>a:hover{background:rgba(255,255,255,0.1)}.wy-nav .wy-menu-vertical header{color:#dd4814}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#dd4814;color:#fff}[data-menu-wrap]{-webkit-transition:all 0.2s ease-in;-moz-transition:all 0.2s ease-in;transition:all 0.2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:left repeat-y #fcfcfc;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxOERBMTRGRDBFMUUxMUUzODUwMkJCOThDMEVFNURFMCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxOERBMTRGRTBFMUUxMUUzODUwMkJCOThDMEVFNURFMCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE4REExNEZCMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE4REExNEZDMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+EwrlwAAAAA5JREFUeNpiMDU0BAgwAAE2AJgB9BnaAAAAAElFTkSuQmCC);background-size:300px 1px}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:absolute;top:0;left:0;width:300px;overflow:hidden;min-height:100%;background:#343131;z-index:200}.wy-nav-top{display:none;background:#dd4814;color:#fff;padding:0.4045em 0.809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:before,.wy-nav-top:after{display:table;content:\"\"}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:bold}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#dd4814;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100x;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,0.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:#999}footer p{margin-bottom:12px}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:before,.rst-footer-buttons:after{display:table;content:\"\"}.rst-footer-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:solid 1px #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:solid 1px #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:gray;font-size:90%}@media screen and (max-width: 768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-nav-content-wrap{margin-left:0!important}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width: 1400px){.wy-nav-content-wrap{background:rgba(0,0,0,0.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,footer,.wy-nav-side{display:none}.wy-nav-content-wrap{margin-left:0}}nav.stickynav{position:fixed;top:0}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:\"Lato\",\"proxima-nova\",\"Helvetica Neue\",Arial,sans-serif;z-index:400}.rst-versions a{color:#dd4814;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:\"\"}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .icon{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}.rst-content img{max-width:100%;height:auto !important}.rst-content div.figure{margin-bottom:24px}.rst-content div.figure.align-center{text-align:center}.rst-content .section>img,.rst-content .section>a>img{margin-bottom:24px}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content .note .last,.rst-content .attention .last,.rst-content .caution .last,.rst-content .danger .last,.rst-content .error .last,.rst-content .hint .last,.rst-content .important .last,.rst-content .tip .last,.rst-content .warning .last,.rst-content .seealso .last,.rst-content .admonition-todo .last{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,0.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent !important;border-color:rgba(0,0,0,0.1) !important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha li{list-style:upper-alpha}.rst-content .section ol p,.rst-content .section ul p{margin-bottom:12px}.rst-content .line-block{margin-left:24px}.rst-content .topic-title{font-weight:bold;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0px 0px 24px 24px}.rst-content .align-left{float:left;margin:0px 24px 24px 0px}.rst-content .align-center{margin:auto;display:block}.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink{display:none;visibility:hidden;font-size:14px}.rst-content h1 .headerlink:after,.rst-content h2 .headerlink:after,.rst-content h3 .headerlink:after,.rst-content h4 .headerlink:after,.rst-content h5 .headerlink:after,.rst-content h6 .headerlink:after,.rst-content dl dt .headerlink:after{visibility:visible;content:\"\";font-family:FontAwesome;display:inline-block}.rst-content h1:hover .headerlink,.rst-content h2:hover .headerlink,.rst-content h3:hover .headerlink,.rst-content h4:hover .headerlink,.rst-content h5:hover .headerlink,.rst-content h6:hover .headerlink,.rst-content dl dt:hover .headerlink{display:inline-block}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:solid 1px #e1e4e5}.rst-content .sidebar p,.rst-content .sidebar ul,.rst-content .sidebar dl{font-size:90%}.rst-content .sidebar .last{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:\"Roboto Slab\",\"ff-tisa-web-pro\",\"Georgia\",Arial,sans-serif;font-weight:bold;background:#e1e4e5;padding:6px 12px;margin:-24px;margin-bottom:24px;font-size:100%}.rst-content .highlighted{background:#F1C40F;display:inline-block;font-weight:bold;padding:0 6px}.rst-content .footnote-reference,.rst-content .citation-reference{vertical-align:super;font-size:90%}.rst-content table.docutils.citation,.rst-content table.docutils.footnote{background:none;border:none;color:#999}.rst-content table.docutils.citation td,.rst-content table.docutils.citation tr,.rst-content table.docutils.footnote td,.rst-content table.docutils.footnote tr{border:none;background-color:transparent !important;white-space:normal}.rst-content table.docutils.citation td.label,.rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}.rst-content table.field-list{border:none}.rst-content table.field-list td{border:none;padding-top:5px}.rst-content table.field-list td>strong{display:inline-block;margin-top:3px}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left;padding-left:0}.rst-content tt{color:#000}.rst-content tt big,.rst-content tt em{font-size:100% !important;line-height:normal}.rst-content tt .xref,a .rst-content tt{font-weight:bold}.rst-content a tt{color:#dd4814}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:bold}.rst-content dl p,.rst-content dl table,.rst-content dl ul,.rst-content dl ol{margin-bottom:12px !important}.rst-content dl dd{margin:0 0 12px 24px}.rst-content dl:not(.docutils){margin-bottom:24px}.rst-content dl:not(.docutils) dt{display:inline-block;margin:6px 0;font-size:90%;line-height:normal;background:#dedede;color:#dd4814;border-top:solid 3px #8ba8af;padding:6px;position:relative}.rst-content dl:not(.docutils) dt:before{color:#8ba8af}.rst-content dl:not(.docutils) dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dl dt{margin-bottom:6px;border:none;border-left:solid 3px #ccc;background:#f0f0f0;color:gray}.rst-content dl:not(.docutils) dl dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dt:first-child{margin-top:0}.rst-content dl:not(.docutils) tt{font-weight:bold}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descclassname{background-color:transparent;border:none;padding:0;font-size:100% !important}.rst-content dl:not(.docutils) tt.descname{font-weight:bold}.rst-content dl:not(.docutils) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:bold}.rst-content dl:not(.docutils) .property{display:inline-block;padding-right:8px}.rst-content .viewcode-link,.rst-content .viewcode-back{display:inline-block;color:#27AE60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:bold}@media screen and (max-width: 480px){.rst-content .sidebar{width:100%}}span[id*='MathJax-Span']{color:#404040}.math{text-align:center}\n/*# sourceMappingURL=theme.css.map */\n"
  },
  {
    "path": "user_guide_src/source/_themes/sphinx_rtd_theme/static/js/oldtheme.js",
    "content": "$( document ).ready(function() {\n    // Shift nav in mobile when clicking the menu.\n    $(document).on('click', \"[data-toggle='wy-nav-top']\", function() {\n      $(\"[data-toggle='wy-nav-shift']\").toggleClass(\"shift\");\n      $(\"[data-toggle='rst-versions']\").toggleClass(\"shift\");\n    });\n    // Close menu when you click a link.\n    $(document).on('click', \".wy-menu-vertical .current ul li a\", function() {\n      $(\"[data-toggle='wy-nav-shift']\").removeClass(\"shift\");\n      $(\"[data-toggle='rst-versions']\").toggleClass(\"shift\");\n    });\n    $(document).on('click', \"[data-toggle='rst-current-version']\", function() {\n      $(\"[data-toggle='rst-versions']\").toggleClass(\"shift-up\");\n    });  \n    // Make tables responsive\n    $(\"table.docutils:not(.field-list)\").wrap(\"<div class='wy-table-responsive'></div>\");\n});\n\nwindow.SphinxRtdTheme = (function (jquery) {\n    var stickyNav = (function () {\n        var navBar,\n            win,\n            stickyNavCssClass = 'stickynav',\n            applyStickNav = function () {\n                if (navBar.height() <= win.height()) {\n                    navBar.addClass(stickyNavCssClass);\n                } else {\n                    navBar.removeClass(stickyNavCssClass);\n                }\n            },\n            enable = function () {\n                applyStickNav();\n                win.on('resize', applyStickNav);\n            },\n            init = function () {\n                navBar = jquery('nav.wy-nav-side:first');\n                win    = jquery(window);\n            };\n        jquery(init);\n        return {\n            enable : enable\n        };\n    }());\n    return {\n        StickyNav : stickyNav\n    };\n}($));\n"
  },
  {
    "path": "user_guide_src/source/_themes/sphinx_rtd_theme/static/js/theme.js",
    "content": "$(document).ready(function () {\n    // Shift nav in mobile when clicking the menu.\n    $(document).on('click', \"[data-toggle='wy-nav-top']\", function () {\n        $(\"[data-toggle='wy-nav-shift']\").toggleClass(\"shift\");\n        $(\"[data-toggle='rst-versions']\").toggleClass(\"shift\");\n    });\n    // Close menu when you click a link.\n    $(document).on('click', \".wy-menu-vertical .current ul li a\", function () {\n        $(\"[data-toggle='wy-nav-shift']\").removeClass(\"shift\");\n        $(\"[data-toggle='rst-versions']\").toggleClass(\"shift\");\n    });\n    $(document).on('click', \"[data-toggle='rst-current-version']\", function () {\n        $(\"[data-toggle='rst-versions']\").toggleClass(\"shift-up\");\n    });\n    // Make tables responsive\n    $(\"table.docutils:not(.field-list)\").wrap(\"<div class='wy-table-responsive'></div>\");\n    // ---\n    // START DOC MODIFICATION BY RUFNEX\n    // v1.0 04.02.2015\n    // Add ToogleButton to get FullWidth-View by Johannes Gamperl codeigniter.de\n\n    $('#openToc').click(function () {\n        $('#nav').slideToggle();\n    });\n    $('#closeMe').click(function () {\n        if (getCookie('ciNav') != 'yes') {\n            setCookie('ciNav', 'yes', 365);\n        } else {\n            setCookie('ciNav', 'no', 365);\n        }\n        tocFlip();\n    });\n    var tocFlip = function(){\n        if (getCookie('ciNav') == 'yes') {\n            $('#nav2').show();\n            $('#topMenu').remove();\n            $('body').css({ background: 'none' });\n            $('.wy-nav-content-wrap').css({ background: 'none', 'margin-left': 0 });\n            $('.wy-breadcrumbs').append('<div style=\"float:right;\"><div style=\"float:left;\" id=\"topMenu\">' + $('.wy-form').parent().html() + '</div></div>');\n            $('.wy-nav-side').toggle();\n        } else {\n            $('#topMenu').remove();\n            $('#nav').hide();\n            $('#nav2').hide();\n            $('body').css({ background: '#edf0f2;' });\n            $('.wy-nav-content-wrap').css({ background: 'none repeat scroll 0 0 #fcfcfc;', 'margin-left': '300px' });\n            $('.wy-nav-side').show();\n        }\n    };\n    if (getCookie('ciNav') == 'yes')\n    {\n        tocFlip();\n        //$('#nav').slideToggle();\n    }\n    // END MODIFICATION ---\n\n});\n\n// Rufnex Cookie functions\nfunction setCookie(cname, cvalue, exdays) {\n    // expire the old cookie if existed to avoid multiple cookies with the same name\n    if  (getCookie(cname)) {\n        document.cookie = cname + \"=;expires=Thu, 01 Jan 1970 00:00:00 GMT\";\n    }\n    var d = new Date();\n    d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));\n    var expires = \"expires=\" + d.toGMTString();\n    document.cookie = cname + \"=\" + cvalue + \"; \" + expires + \"; path=/\";\n}\nfunction getCookie(cname) {\n    var name = cname + \"=\";\n    var ca = document.cookie.split(';');\n    for (var i = 0; i < ca.length; i++) {\n        var c = ca[i];\n        while (c.charAt(0) == ' ')\n            c = c.substring(1);\n        if (c.indexOf(name) == 0) {\n            return c.substring(name.length, c.length);\n        }\n    }\n    return '';\n}\n// End\n\n// resize window\n$(window).on('resize', function(){\n    // show side nav on small screens when pulldown is enabled\n    if (getCookie('ciNav') == 'yes' && $(window).width() <= 768) { // 768px is the tablet size defined by the theme\n        $('.wy-nav-side').show();\n    }\n    // changing css with jquery seems to override the default css media query\n    // change margin\n    else if (getCookie('ciNav') == 'no' && $(window).width() <= 768) {\n        $('.wy-nav-content-wrap').css({'margin-left': 0});\n    }\n    // hide side nav on large screens when pulldown is enabled\n    else if (getCookie('ciNav') == 'yes' && $(window).width() > 768) {\n        $('.wy-nav-side').hide();\n    }\n    // change margin\n    else if (getCookie('ciNav') == 'no' && $(window).width() > 768) {\n        $('.wy-nav-content-wrap').css({'margin-left': '300px'});\n    }\n});\n\nwindow.SphinxRtdTheme = (function (jquery) {\n    var stickyNav = (function () {\n        var navBar,\n                win,\n                stickyNavCssClass = 'stickynav',\n                applyStickNav = function () {\n                    if (navBar.height() <= win.height()) {\n                        navBar.addClass(stickyNavCssClass);\n                    } else {\n                        navBar.removeClass(stickyNavCssClass);\n                    }\n                },\n                enable = function () {\n                    applyStickNav();\n                    win.on('resize', applyStickNav);\n                },\n                init = function () {\n                    navBar = jquery('nav.wy-nav-side:first');\n                    win = jquery(window);\n                };\n        jquery(init);\n        return {\n            enable: enable\n        };\n    }());\n    return {\n        StickyNav: stickyNav\n    };\n}($));\n"
  },
  {
    "path": "user_guide_src/source/_themes/sphinx_rtd_theme/theme.conf",
    "content": "[theme]\ninherit = basic\nstylesheet = css/citheme.css\n\n[options]\ntypekit_id = hiw1hhg\nanalytics_id = \nsticky_navigation = False\n"
  },
  {
    "path": "user_guide_src/source/_themes/sphinx_rtd_theme/versions.html",
    "content": "{% if READTHEDOCS %}\n{# Add rst-badge after rst-versions for small badge style. #}\n  <div class=\"rst-versions\" data-toggle=\"rst-versions\" role=\"note\" aria-label=\"versions\">\n    <span class=\"rst-current-version\" data-toggle=\"rst-current-version\">\n      <span class=\"fa fa-book\"> Read the Docs</span>\n      v: {{ current_version }}\n      <span class=\"fa fa-caret-down\"></span>\n    </span>\n    <div class=\"rst-other-versions\">\n      <dl>\n        <dt>Versions</dt>\n        {% for slug, url in versions %}\n          <dd><a href=\"{{ url }}\">{{ slug }}</a></dd>\n        {% endfor %}\n      </dl>\n      <dl>\n        <dt>Downloads</dt>\n        {% for type, url in downloads %}\n          <dd><a href=\"{{ url }}\">{{ type }}</a></dd>\n        {% endfor %}\n      </dl>\n      <dl>\n        <dt>On Read the Docs</dt>\n          <dd>\n            <a href=\"//{{ PRODUCTION_DOMAIN }}/projects/{{ slug }}/?fromdocs={{ slug }}\">Project Home</a>\n          </dd>\n          <dd>\n            <a href=\"//{{ PRODUCTION_DOMAIN }}/builds/{{ slug }}/?fromdocs={{ slug }}\">Builds</a>\n          </dd>\n      </dl>\n      <hr/>\n      Free document hosting provided by <a href=\"https://www.readthedocs.org\">Read the Docs</a>.\n\n    </div>\n  </div>\n{% endif %}\n\n"
  },
  {
    "path": "user_guide_src/source/changelog.rst",
    "content": "##########\nChange Log\n##########\n\nVersion 3.2.0\n=============\n\nRelease Date: Not Released\n\n-  General Changes\n\n   -  Officially dropped any kind of support for anything under PHP 5.4.8.\n   -  Updated Welcome view and HTML error templates with new styling.\n   -  Updated configurable directory paths to handle missing trailing ``DIRECTORY_SEPARATOR`` automatically.\n   -  Added support for HTTP status codes 103, 207, 308, 421 and 451 to :doc:`common function <general/common_functions>` :php:func:`set_status_header()`.\n\n-  Core\n\n   -  Added a ``$config['log_file']`` option.\n   -  Removed ``$config['log_file_extension']``.\n   -  Removed ``$config['rewrite_short_tags']`` (irrelevant on PHP 5.4+).\n   -  Removed previously deprecated ``$config['global_xss_filtering']``.\n   -  Removed previously deprecated :doc:`Routing Class <general/routing>` methods ``fetch_directory()``, ``fetch_class()`` and ``fetch_method()`` (use the respective class properties instead).\n   -  Removed previously deprecated :doc:`Config Library <libraries/config>` method ``system_url()`` (encourages insecure practices).\n   -  Changed :doc:`URI Library <libraries/uri>` to ignore the ``$config['url_suffix']``, ``$config['permitted_uri_chars']`` configuration settings for CLI requests.\n   -  Changed :doc:`Loader <libraries/loader>` method ``model()`` to always check if the loaded class extends ``CI_Model``.\n   -  Changed :doc:`Output Library <libraries/output>` method ``_display()`` default parameter value to ``NULL`` instead of empty string.\n\n   -  :doc:`Input Library <libraries/input>` changes include:\n\n      - Removed previously deprecated ``$config['allow_get_array']``.\n      - Removed previously deprecated ``$config['standardize_newlines']``.\n      - Removed previously deprecated method ``is_cli_request()`` (use :php:func:`is_cli()` instead).\n      - Changed the ``set_cookie()`` method's default expiry time to 0 (expires when browser is closed).\n      - Changed the ``set_cookie()`` method to delete the cookie if a negative expiry time is passed to it.\n      - Allowed usage of nested array keys in ``get_post()``, ``post_get()`` methods.\n\n-  Libraries\n\n   -  Removed previously deprecated *Encrypt Library*.\n   -  Removed previously deprecated *Cart Library*.\n   -  Removed previously deprecated *Javascript Library* (it was always experimental in the first place).\n   -  Removed previously deprecated ``anchor_class`` option from :doc:`Pagination Library <libraries/pagination>`.\n   -  Added TLS and UNIX socket connection support to :doc:`Session Library <libraries/sessions>` 'redis' driver.\n   -  Updated :doc:`ZIP Library <libraries/zip>` method ``read_dir()`` to include hidden (dot-prefixed) files.\n\n   -  :doc:`Cache Library <libraries/caching>` changes include:\n\n      - Added 'apcu' driver.\n      - Added UNIX socket connection support to the 'memcached' driver.\n      - Added 'database' configuration option to the 'redis' driver, allowing to auto-select another database.\n      - Added method ``get_loaded_driver()`` to return the currently used driver.\n      - Changed the 'memcached' driver to ignore configurations that don't specify a hostname.\n      - Removed the *socket_type* configuration setting from the 'redis' driver.\n      - Changed data serialization logic in 'redis' driver for better performance.\n\n   -  :doc:`Form Validation Library <libraries/form_validation>` changes include:\n\n      - Removed previously deprecated method ``prep_for_form()`` / rule *prep_for_form*.\n      - Removed previously deprecated ability to use language translations without the ``'form_validation_'`` prefix.\n      - Changed method ``set_rules()`` to throw a ``BadMethodCallException`` when its first parameter is not an array and the ``$rules`` one is unused.\n      - Added rule **valid_mac**, which replicates PHP's native ``filter_var()`` with ``FILTER_VALIDATE_MAC``.\n      - Added ability to validate entire arrays at once, if ``is_array`` is within the list of rules.\n      - Added ability to fetch processed data via a second parameter to ``run()``.\n\n   -  :doc:`HTML Table Library <libraries/table>` changes include:\n\n      - Changed method ``clear()`` to also reset captions.\n\n   -  :doc:`Email Library <libraries/email>` changes include:\n\n      - Changed the default value of the **validate** option to ``TRUE``.\n      - Changed the ``send()`` method to always return ``TRUE`` when sending multiple batches of emails.\n\n\n   -  :doc:`Image Manipulation Library <libraries/image_lib>` changes include:\n\n      - Added option to change the output filename via the ``new_name`` option when rendering images with ``dynamic_output``.\n      - Updated to serve multibyte filenames when rendering images with ``dynamic_output``, if possible.\n      - Added WebP image support.\n\n-  :doc:`Database <database/index>` changes include:\n\n   -  Removed the option to disable the :doc:`Query Builder <database/query_builder>`.\n   -  Removed driver-specific ``$curs_id`` property and ``get_cursor()``, ``stored_procedure()`` methods from OCI8 driver.\n   -  Removed previously deprecated 'sqlite' driver (used for SQLite version 2; no longer shipped with PHP 5.4+).\n   -  Removed method ``db_set_charset()`` and the ability to change a connection character set at runtime.\n   -  Changed method ``initialize()`` to return void and instead throw a ``RuntimeException`` in case of failure.\n   -  Changed method ``db_connect()`` to always set the connection character set (if supported by the driver) and to fail if it can't.\n\n   -  :doc:`Database Forge <database/forge>`:\n\n      - Added support for declaring date/time type fields default values as ``CURRENT_TIMESTAMP`` and similar.\n      - Removed previously deprecated ``$_after`` parameter for ``add_column()``.\n\n   -  :doc:`Query Builder <database/query_builder>`:\n\n      - Added methods ``having_in()``, ``or_having_in()``, ``not_having_in()``, ``or_not_having_in()``.\n      - Updated methods ``where_in()``, ``or_where_in()``, ``not_where_in()``, ``or_not_where_in()`` to reject non-array inputs for the second parameter.\n      - Updated method ``join()`` to allow accepting ``NATURAL`` clauses in its third parameter.\n      - Updated logic to allow dots in alias names.\n\n-  Helpers\n\n   -  Removed previously deprecated *Email Helper* (had only two functions, aliases for PHP's native ``filter_var()`` and ``mail()``).\n   -  Removed previously deprecated *Smiley Helper*.\n   -  Removed previously deprecated :doc:`Date Helper <helpers/date_helper>` function ``standard_date()`` (use PHP's native ``date()`` instead).\n   -  Removed previously deprecated :doc:`Date Helper <helpers/date_helper>` function ``nice_date()`` (use PHP's native ``DateTime::format()`` instead).\n   -  Removed previously deprecated :doc:`Security Helper <helpers/security_helper>` function ``do_hash()`` (use PHP's native ``hash()`` instead).\n   -  Removed previously deprecated :doc:`File Helper <helpers/file_helper>` function ``read_file()`` (use PHP's native ``file_get_contents()`` instead).\n   -  Removed previously deprecated options ``'dash'`` and ``'underscore'`` from :doc:`URL Helper <helpers/url_helper>` function :php:func:`url_title()`.\n   -  Added new function :php:func:`ordinal_format()` to :doc:`Inflector Helper <helpers/inflector_helper>`.\n\n   -  :doc:`Download Helper <helpers/download_helper>` changes include:\n\n      - Updated :php:func:`force_download()` to allow existing files to be renamed for download.\n      - Updated :php:func:`force_download()` to better utilize available server memory.\n      - Updated :php:func:`force_download()` to serve multibyte filenames when possible, via the ``filename*`` attribute specified by IETF `RFC 6266 <https://tools.ietf.org/html/rfc6266>`_.\n\n   -  :doc:`String Helper <helpers/string_helper>` changes include:\n\n      - Removed previously deprecated function ``trim_slashes()`` (use PHP's native ``trim()`` with ``'/'`` instead).\n      - Removed previously deprecated function ``repeater()`` (use PHP's native ``str_repeat()`` instead).\n      - Removed previously deprecated ``'unique'`` and ``'encrypt'`` options from ``random_string()``.\n\n   -  :doc:`HTML Helper <helpers/html_helper>` changes include:\n\n      - Removed previously deprecated function ``br()`` (use PHP's native ``str_repeat()`` with ``'<br />'`` instead).\n      - Removed previously deprecated function ``nbs()`` (use PHP's native ``str_repeat()`` with ``'&nbsp;'`` instead).\n      - Updated function :php:func:`meta()` with support for \"charset\" and \"property\" properties.\n      - Changed function :php:func:`doctype()` default document type to HTML 5.\n\n   -  :doc:`Form Helper <helpers/form_helper>` changes include:\n\n      - Removed previously deprecated function ``form_prep()`` (use :php:func:`html_escape()` instead).\n      - Removed the second (out of three) parameter from the :php:func:`form_upload()` function (it was never used).\n\n   -  :doc:`CAPTCHA Helper <helpers/captcha_helper>` changes include:\n\n      - Added 'img_alt' option with a default value of 'captcha'.\n      - Added 'img_class' option.\n      - Added ability to generate ``data:image/png;base64`` URIs instead of writing image files to disk.\n      - Updated to always create PNG images instead of JPEG.\n      - Removed previously deprecated usage with ``$img_path``, ``$img_url``, ``$font_path`` as extra parameters instead of array options.\n\nBug fixes for 3.2.0\n===================\n\n-  Fixed a bug (#5562) - :doc:`Cache Library <libraries/caching>` 'redis' driver would pointlessly attempt to perform its functions if a connection to the Redis server failed.\n-  Fixed a bug (#92) - :doc:`File Helper <helpers/file_helper>` function :php:func:`get_dir_file_info()` output could have colliding array keys.\n\nVersion 3.1.13\n==============\n\nRelease Date: Mar 3, 2022\n\nBug fixes for 3.1.13\n====================\n\n-  Fixed a bug (#6107) - :doc:`Session Library <libraries/sessions>` broke for PHP 5 due to a misnamed polyfill interface.\n\nVersion 3.1.12\n==============\n\nRelease Date: Mar 3, 2022\n\n- **Security**\n\n   -  Fixed a possible session fixation vulnerability where ``session.use_strict_mode`` wasn't enforced on PHP 7+.\n\n-  General Changes\n\n   -  Improved logging of error conditions in :doc:`CAPTCHA Helper <helpers/captcha_helper>` function :php:func:`create_captcha()`.\n   -  Added ``AUTO_INCREMENT`` support for Oracle 12.1+ to :doc:`Database Forge <database/forge>`.\n   -  Added ``FULL [OUTER] JOIN`` support to :doc:`Query Builder <database/query_builder>`.\n   -  Added support for detecting WebP image type to :doc:`File Uploading Library <libraries/file_uploading>`.\n   -  Added method :doc:`Database Library <database/index>` method ``trans_active()`` to expose transaction state.\n   -  Updated :doc:`Database Library <database/index>` 'pdo' driver to attempt to free resources in order to allow connections to be closed.\n   -  Added ``SameSite=Strict`` attribute to the CSRF cookie sent by the :doc:`Security Class <libraries/security>`.\n   -  Added ``$config['cookie_samesite']`` option and ``$samesite`` parameter to :doc:`Input Library <libraries/input>` method ``set_cookie()``.\n   -  Added ``SameSite`` support through ``$config['sess_samesite']`` option to the :doc:`Session Library <libraries/sessions>`.\n   -  Added a wrapper class around :doc:`Session <libraries/sessions>` drivers to deal with compatibility between PHP 8.1 and older versions.\n   -  Updated a lot of code for PHP 8.0 and 8.1 compatibility.\n\nBug fixes for 3.1.12\n====================\n\n-  Fixed a bug (#5834) - :doc:`Query Builder <database/query_builder>` method ``count_all_results()`` triggered an SQL error for queries with a ``HAVING`` clause.\n-  Fixed a bug (#5840) - :doc:`Cache Library <libraries/caching>` 'redis' driver triggered an ``E_DEPRECATED`` warning about ``sRemove()`` with phpRedis 5.\n-  Fixed a bug (#5857) - :doc:`Session <libraries/sessions>` data could be corrupted after a concurrent request write with the 'files' driver due to a filesize cache being incorrect.\n-  Fixed a bug (#5861) - :doc:`Cache Library <libraries/caching>` 'redis' driver would always use phpRedis 5 ``del()`` due to an incorrect version check.\n-  Fixed a bug (#5879) - :doc:`Profiler Library <general/profiling>` triggered an ``E_DEPRECATED`` warning on PHP 7.4+.\n-  Fixed a bug (#5901) - :doc:`Database Library <database/index>` methods ``list_fields()`` and ``field_data()`` ignored the configured table schema on PostgreSQL.\n-  Fixed a bug (#5906) - :doc:`Database Library <database/index>` 'postgre' driver couldn't use the failover feature without a ``$config['dsn']``.\n-  Fixed a bug (#5903) - :doc:`common function <general/common_functions>` :php:func:`set_status_header()` didn't recognize 'HTTP/2.0' as a valid ``$_SERVER['SERVER_PROTOCOL']``.\n-  Fixed a bug (#6013) - :doc:`Session <libraries/sessions>` flashdata didn't work on PHP 8.\n-  Fixed a bug (#6006) - ``is_callable()`` change in PHP 8 broke :doc:`Migrations <libraries/migration>`, a part of :doc:`XML-RPC <libraries/xmlrpc>` and an edge case in 404 detection logic.\n-  Fixed a bug (#5729) - :doc:`Query Builder <database/query_builder>` possibly not detecting ``NOT BETWEEN`` expression.\n\nVersion 3.1.11\n==============\n\nRelease Date: Sep 19, 2019\n\n-  General Changes\n\n   -  Changed ``CI_Log`` to append ``PHP_EOL`` instead of ``\\n`` at the end of log messages.\n   -  Improved performance in :doc:`Cache Library <libraries/caching>` 'redis' driver with non-scalar variables.\n   -  Altered the :doc:`Session Library <libraries/sessions>` 'files' driver to log error and trigger a session start failure instead of throwing an ``Exception`` in case of unusable ``$config['sess_save_path']``.\n   -  Updated the :doc:`Session <libraries/sessions>` and :doc:`Cache <libraries/caching>` libraries' 'redis' driver to work with phpRedis 5.\n\nBug fixes for 3.1.11\n--------------------\n\n-  Fixed a bug (#5681) - :doc:`Database Forge <database/forge>` method ``modify_column()`` produced erroneous SQL for ``DEFAULT`` attribute changes under PostgreSQL, Firebird.\n-  Fixed a bug (#5692) - :doc:`Database Forge <database/forge>` didn't handle column nullability with the 'oci8', 'pdo/oci' drivers.\n-  Fixed a bug (#5701) - :doc:`Database <database/index>` driver 'pdo/pgsql' produced incorrect DSNs when constructing from a configuration array.\n-  Fixed a bug (#5708) - :doc:`Session Library <libraries/sessions>` 'redis' driver too often failed with locking-related errors that could've been avoided.\n-  Fixed a bug (#5703) - :doc:`Session Library <libraries/sessions>` triggered an ``E_WARNING`` message about changing ``session.save_path`` during an active session when it fails to obtain a lock.\n-  Fixed a bug where :doc:`Session Library <libraries/sessions>` 'database' driver didn't trigger a failure if it can't obtain a lock.\n-  Fixed a bug (#5755) - :doc:`Form Validation Library <libraries/form_validation>` rule **valid_url** accepted digit-only domains due to a PHP bug.\n-  Fixed a bug (#5753) - :doc:`Cache Library <libraries/caching>` 'redis' driver methods ``increment()``, ``decrement()`` ignored their ``$offset`` parameter.\n-  Fixed a bug (#5779) - :doc:`Session Library <libraries/sessions>` 'redis' only attempted to validate session IDs in case the connection to Redis failed.\n-  Fixed a bug (#5774) - :doc:`Database Results <database/results>` method ``custom_result_object()`` didn't properly handle empty result sets, triggering ``E_WARNING`` messages on PHP 7.2+.\n-  Fixed a bug (#5788) - :doc:`Database Results <database/results>` method ``field_data()`` triggered an ``E_NOTICE`` error with PDO when a field type is not recognized by PHP.\n-  Fixed a bug (#5796) - :doc:`Query Builder <database/query_builder>` method ``list_tables()`` triggered an SQL syntax error under MySQL when the database schema is a numeric string.\n-  Fixed a bug where :doc:`Security Class <libraries/security>` would trigger an ``E_WARNING`` if CSRF inputs are arrays instead of strings.\n\nVersion 3.1.10\n==============\n\nRelease Date: Jan 16, 2019\n\n-  General Changes\n\n   -  Added 'ssl_verify' support to the 'pdo/mysql' :doc:`Database <database/index>` driver.\n   -  Renamed :doc:`Inflector Helper <helpers/inflector_helper>` function ``is_countable()`` to :php:func:`word_is_countable()` due to the former colliding with one introduced in PHP 7.3.0.\n\nBug fixes for 3.1.10\n--------------------\n\n-  Fixed a bug (#5526) - :doc:`Session Library <libraries/sessions>` had a syntax error in its 'memcached' driver.\n-  Fixed a bug (#5542) - :doc:`Database Forge <database/forge>` method ``modify_column()`` always made fields ``NOT NULL`` when attempting to modify their nullable property under PostgreSQL.\n-  Fixed a bug (#5561) - :doc:`Database Library <database/index>` didn't allow SSL connection configuration with only the 'ssl_verify' option when using the 'mysqli' driver.\n-  Fixed a bug (#5545) - :doc:`Session Library <libraries/sessions>` crashed due to a caching-related error with the 'files' driver.\n-  Fixed a bug (#5571) - :doc:`XML-RPC Library <libraries/xmlrpc>` had a typo that triggered an ``E_WARNING`` message on PHP 7.2.\n-  Fixed a bug (#5587) - :doc:`Database Forge <database/forge>` method ``create_table()`` generated an ``E_WARNING`` message.\n-  Fixed a bug (#5590) - :doc:`Form Validation Library <libraries/form_validation>` rule **valid_base64** didn't have a default error message.\n-  Fixed a bug (#5624) - :doc:`Database Library <database/index>` methods ``list_fields()``, ``field_exists()`` returned incorrect results after tables are modified.\n-  Fixed a bug (#5627) - :doc:`Database <database/index>` driver 'mysqli' triggered an ``E_WARNING`` message if there's no ``'port'`` specified in the database configuration.\n-  Fixed a bug (#5651) - :doc:`Database Caching <database/caching>` could try to delete non-existent cache files due to a race condition.\n-  Fixed a bug (#5652) - :doc:`CAPTCHA Helper <helpers/captcha_helper>` function :php:func:`create_captcha()` didn't comply with CSS standards.\n-  Fixed a bug (#5605) - :doc:`Form Validation Library <libraries/form_validation>` didn't nullify array inputs that are expected to be strings.\n\nVersion 3.1.9\n=============\n\nRelease Date: Jun 12, 2018\n\n- **Security**\n\n   -  Updated :doc:`URL Helper <helpers/url_helper>` function :php:func:`auto_link()` to add ``rel=\"noopener\"`` to generated links in order to prevent tab hijacking.\n   -  Fixed a possible session fixation vulnerability where the :doc:`Session Library <libraries/sessions>` enabled ``session.use_strict_mode`` but it didn't actually do anything (thanks to Aamer Shah, Prasanna Kumar).\n\n-  General Changes\n\n   -  Updated :doc:`Query Builder <database/query_builder>` method ``limit()`` to allow ``0`` values.\n   -  Updated :doc:`Email Library <libraries/email>` and :doc:`Form Validation Library <libraries/form_validation>` to discard the results of failed ``idn_to_ascii()`` calls while validating e-mail addresses.\n\nBug fixes for 3.1.9\n-------------------\n\n-  Fixed a regression (#5448) - :doc:`Query Builder <database/query_builder>` methods ``like()``, ``or_like()`` (and siblings) didn't apply *dbprefix* or identifier escaping.\n-  Fixed a regression (#5462) - :doc:`Query Builder <database/query_builder>` methods ``like()``, ``or_like()`` (and siblings) produced incorrect SQL syntax when used with ``'before'`` as the third parameter.\n-  Fixed a bug (#5516) - :doc:`HTML Helper <helpers/html_helper>` functions :php:func:`img()`, :php:func:`link_tag()` would output results with double slashes if a prefix slash was included in their path inputs.\n\nVersion 3.1.8\n=============\n\nRelease Date: Mar 22, 2018\n\n- **Security**\n\n   -  Updated :doc:`Security Library <libraries/security>` method ``xss_clean()`` to also filter JavaScript tag functions.\n   -  Fixed a bug where :doc:`Security Library <libraries/security>` method ``xss_clean()`` didn't check for parentheses around JavaScript's ``document``.\n\n-  General Changes\n\n   -  Updated :doc:`Email Library <libraries/email>` to always negotiate between TLS 1.0, 1.1, 1.2 when possible (PHP 5.6+) for SMTP connections.\n   -  Updated :doc:`Database Library <database/index>` method ``version()`` to exclude suffixes to the main version numbers with the 'postgre' driver.\n\nBug fixes for 3.1.8\n-------------------\n\n-  Fixed a bug where :doc:`Form Validation Library <libraries/form_validation>`, :doc:`Email Library <libraries/email>` tried to use ``INTL_IDNA_VARIANT_UTS46`` when it was undeclared.\n-  Fixed a bug where :doc:`Query Builder <database/query_builder>` methods ``where()``, ``having()`` treated values passed to them as arbitrary SQL.\n-  Fixed a bug (#5423) - :doc:`Database Library <database/index>` method ``insert_id()`` failed due to incorrect server version parsing with the 'postgre' driver.\n-  Fixed a bug (#5425) - :doc:`XML-RPC Library <libraries/xmlrpc>` produced an error message related to ``count()`` on PHP 7.2.\n-  Fixed a bug (#5434) - :doc:`Image Manipulation Library <libraries/image_lib>` attempted to ``chmod()`` while rendering images with the ``dynamic_output`` option.\n-  Fixed a bug (#5435) - :doc:`Database Results <database/results>` method ``field_data()`` hid info about one field if ``limit()`` was previously used with the 'oci8' driver.\n\nVersion 3.1.7\n=============\n\nRelease Date: Jan 13, 2018\n\n-  General Changes\n\n   -  Updated :doc:`Form Validation Library <libraries/form_validation>` rule ``valid_email`` to use ``INTL_IDNA_VARIANT_UTS46`` for non-ASCII domain names.\n   -  Updated :doc:`Email Library <libraries/email>` to use ``INTL_IDNA_VARIANT_UTS46`` for non-ASCII domain names.\n   -  Updated :doc:`Loader Library <libraries/loader>` method ``model()`` to log both ``CI_Model`` class loading and individual models' initialization.\n   -  Updated :doc:`Pagination Library <libraries/pagination>` to preserve previously set attributes while calling ``initialize()``.\n   -  Updated :doc:`Cache Library <libraries/caching>` to automatically add items to cache on ``increment()``, ``decrement()`` calls for missing keys.\n   -  Deprecated usage of :doc:`CAPTCHA Helper <helpers/captcha_helper>` function :php:func:`create_captcha()` with parameters other than ``$data``.\n\nBug fixes for 3.1.7\n-------------------\n\n-  Fixed a regression (#5276) - :doc:`Database Utilities <database/utilities>` method ``backup()`` generated incorrect ``INSERT`` statements with the 'mysqli' driver.\n-  Fixed a regression where :doc:`Database Results <database/results>` method ``field_data()`` returned incorrect type names.\n-  Fixed a bug (#5278) - :doc:`URL Helper <helpers/url_helper>` function :php:func:`auto_link()` didn't detect trailing slashes in URLs.\n-  Fixed a regression (#5282) - :doc:`Query Builder <database/query_builder>` method ``count_all_results()`` breaks ``ORDER BY`` clauses for subsequent queries.\n-  Fixed a bug (#5279) - :doc:`Query Builder <database/query_builder>` didn't account for already escaped identifiers while applying database name prefixes.\n-  Fixed a bug (#5331) - :doc:`URL Helper <helpers/url_helper>` function :php:func:`auto_link()` converted e-mail addresses starting with 'www.' to both \"url\" and \"email\" links.\n-  Fixed a bug where ``$config['allow_get_array']`` defaulted to ``FALSE`` if it didn't exist in the config file.\n-  Fixed a bug (#5379) - :doc:`Session Library <libraries/sessions>` would incorrectly fail to obtain a lock that it already has on PHP 7 with the 'memcached' driver.\n\nVersion 3.1.6\n=============\n\nRelease Date: Sep 25, 2017\n\n-  **Security**\n\n   -  Fixed a potential object injection in :doc:`Cache Library <libraries/caching>` 'apc' driver when ``save()`` is used with ``$raw = TRUE`` (thanks to Tomas Bortoli).\n\n-  General Changes\n\n   -  Deprecated :doc:`Cache Library Library <libraries/caching>` driver 'apc'.\n   -  Updated the :doc:`Session Library <libraries/sessions>` 'redis', 'memcached' drivers to reduce the potential of a locking race conditions.\n\n\nBug fixes for 3.1.6\n-------------------\n\n-  Fixed a bug (#5164) - :doc:`Loader Library <libraries/loader>` method ``library()`` ignored requests to load libraries previously assigned to super-object properties named differently than the library name.\n-  Fixed a bug (#5168) - :doc:`Query Builder <database/query_builder>` method ``count_all_results()`` produced erroneous queries on Microsoft SQL Server when ``ORDER BY`` clauses are cached.\n-  Fixed a bug (#5128) - :doc:`Profiler <general/profiling>` didn't wrap ``$_SESSION`` and configuration arrays in ``<pre>`` tags.\n-  Fixed a bug (#5183) - :doc:`Database Library <database/index>` method ``is_write_type()`` didn't return TRUE for ``MERGE`` statements.\n-  Fixed a bug where :doc:`Image Manipulation Library <libraries/image_lib>` didn't escape image source paths passed to NetPBM as shell arguments.\n-  Fixed a bug (#5236) - :doc:`Query Builder <database/query_builder>` methods ``limit()``, ``offset()`` break SQL Server 2005, 2008 queries with ``\"<tablename>\".*`` in the ``SELECT`` clause.\n-  Fixed a bug (#5243) - :doc:`Database Library <database/index>` method ``version()`` didn't work with the 'pdo/dblib' driver.\n-  Fixed a bug (#5246) - :doc:`Database transactions <database/transactions>` status wasn't reset unless ``trans_complete()`` was called.\n-  Fixed a bug (#5260) - :doc:`Database Utilities <database/utilities>` method ``backup()`` generated incorrect ``INSERT`` statements with the 'mysqli' driver.\n-  Fixed a bug where :doc:`Database Results <database/results>` method ``field_data()`` didn't parse field types with the 'mysqli' driver.\n\nVersion 3.1.5\n=============\n\nRelease Date: Jun 19, 2017\n\n-  **Security**\n\n   -  :doc:`Form Validation Library <libraries/form_validation>` rule ``valid_email`` could be bypassed if ``idn_to_ascii()`` is available.\n\n-  General Changes\n\n   -  Updated :doc:`Form Helper <helpers/form_helper>` function :php:func:`form_label()` to accept HTML attributes as a string.\n\nBug fixes for 3.1.5\n-------------------\n\n-  Fixed a bug (#5070) - :doc:`Email Library <libraries/email>` didn't properly detect 7-bit encoding.\n-  Fixed a bug (#5084) - :doc:`XML-RPC Library <libraries/xmlrpc>` errored because of a variable name typo.\n-  Fixed a bug (#5108) - :doc:`Inflector Helper <helpers/inflector_helper>` function :php:func:`singular()` didn't properly handle 'quizzes'.\n-  Fixed a regression (#5131) - private controller methods triggered PHP errors instead of a 404 response.\n-  Fixed a bug (#5150) - :doc:`Database Forge <database/forge>` method ``modify_column()`` triggered an error while renaming columns with the 'oci8', 'pdo/oci' drivers.\n-  Fixed a bug (#5155) - :doc:`Query Builder <database/query_builder>` method ``count_all_results()`` returned incorrect result for queries using ``LIMIT``, ``OFFSET``.\n\nVersion 3.1.4\n=============\n\nRelease Date: Mar 20, 2017\n\n-  **Security**\n\n   -  Fixed a header injection vulnerability in :doc:`common function <general/common_functions>` :php:func:`set_status_header()` under Apache (thanks to Guillermo Caminer from `Flowgate <https://flowgate.net/>`_).\n   -  Fixed byte-safety issues in **Encrypt Library** (DEPRECATED) when ``mbstring.func_overload`` is enabled.\n   -  Fixed byte-safety issues in :doc:`Encryption Library <libraries/encryption>` when ``mbstring.func_overload`` is enabled.\n   -  Fixed byte-safety issues in :doc:`compatibility functions <general/compatibility_functions>` ``password_hash()``, ``hash_pbkdf2()`` when ``mbstring.func_overload`` is enabled.\n   -  Updated **Encrypt Library** (DEPRECATED) to call ``mcrypt_create_iv()`` with ``MCRYPT_DEV_URANDOM``.\n\n-  General Changes\n\n   -  Updated the :doc:`Image Manipulation Library <libraries/image_lib>` to work-around an issue with some JPEGs when using GD.\n\nBug fixes for 3.1.4\n-------------------\n\n-  Fixed a regression (#4975) - :doc:`Loader Library <libraries/loader>` couldn't handle objects passed as view variables.\n-  Fixed a bug (#4977) - :doc:`Loader Library <libraries/loader>` method ``helper()`` could accept any character as a filename extension separator.\n-  Fixed a regression where the :doc:`Session Library <libraries/sessions>` would fail on a ``session_regenerate_id(TRUE)`` call with the 'database' driver.\n-  Fixed a bug (#4987) - :doc:`Query Builder <database/query_builder>` caching didn't keep track of table aliases.\n-  Fixed a bug where :doc:`Text Helper <helpers/text_helper>` function ``ascii_to_entities()`` wasn't byte-safe when ``mbstring.func_overload`` is enabled.\n-  Fixed a bug where ``CI_Log``, ``CI_Output``, ``CI_Email`` and ``CI_Zip`` didn't handle strings in a byte-safe manner when ``mbstring.func_overload`` is enabled.\n-  Fixed a bug where :doc:`Session Library <libraries/sessions>` didn't read session data in a byte-safe manner when ``mbstring.func_overload`` is enabled.\n-  Fixed a bug (#4990) - :doc:`Profiler <general/profiling>` didn't close ``<pre>`` tags it generated.\n-  Fixed a bug (#4990) - :doc:`Profiler <general/profiling>` didn't HTML-escape quotes for ``$_SESSION`` variables.\n-  Fixed a bug where :doc:`Input Library <libraries/input>` method ``set_cookie()`` didn't allow its *httponly* and *secure* parameters to be overriden to ``FALSE``.\n-  Fixed a bug (#5006) - :doc:`common function <general/common_functions>` :php:func:`get_mimes()` didn't load *application/config/mimes.php* if an environment specific config exists.\n-  Fixed a bug (#5006) - :doc:`common function <general/common_functions>` :php:func:`remove_invisible_characters()` didn't remove URL-encoded ``0x7F``.\n-  Fixed a bug (#4815) - :doc:`Database Library <database/index>` stripped URL-encoded sequences while escaping strings with the 'mssql' driver.\n-  Fixed a bug (#5044) - :doc:`HTML Helper <helpers/html_helper>` function :php:func:`img()` didn't accept ``data:`` URI schemes for the image source.\n-  Fixed a bug (#5050) - :doc:`Database Library <database/index>` tried to access an undefined property in a number of error handling cases.\n-  Fixed a bug (#5057) - :doc:`Database <database/index>` driver 'postgre' didn't actually apply extra options (such as 'connect_timeout') to its DSN.\n\nVersion 3.1.3\n=============\n\nRelease Date: Jan 09, 2017\n\n-  **Security**\n\n   -  Fixed an XSS vulnerability in :doc:`Security Library <libraries/security>` method ``xss_clean()``.\n   -  Fixed a possible file inclusion vulnerability in :doc:`Loader Library <libraries/loader>` method ``vars()``.\n   -  Fixed a possible remote code execution vulnerability in the :doc:`Email Library <libraries/email>` when 'mail' or 'sendmail' are used (thanks to Paul Buonopane from `NamePros <https://www.namepros.com/>`_).\n   -  Added protection against timing side-channel attacks in :doc:`Security Library <libraries/security>` method ``csrf_verify()``.\n   -  Added protection against BREACH attacks targeting the CSRF token field generated by :doc:`Form Helper <helpers/form_helper>` function :php:func:`form_open()`.\n\n-  General Changes\n\n   -  Deprecated ``$config['allow_get_array']``.\n   -  Deprecated ``$config['standardize_newlines']``.\n   -  Deprecated :doc:`Date Helper <helpers/date_helper>` function ``nice_date()``.\n\nBug fixes for 3.1.3\n-------------------\n\n-  Fixed a bug (#4886) - :doc:`Database Library <database/index>` didn't differentiate bind markers inside double-quoted strings in queries.\n-  Fixed a bug (#4890) - :doc:`XML-RPC Library <libraries/xmlrpc>` didn't work on PHP 7.\n-  Fixed a regression (#4887) - :doc:`File Uploading Library <libraries/file_uploading>` triggered fatal errors due to numerous PHP distribution channels (XAMPP and cPanel confirmed) explicitly disabling ext/fileinfo by default.\n-  Fixed a bug (#4679) - :doc:`Input Library <libraries/input>` method ``ip_address()`` didn't properly resolve ``$config['proxy_ips']`` IPv6 addresses.\n-  Fixed a bug (#4902) - :doc:`Image Manipulation Library <libraries/image_lib>` processing via ImageMagick didn't work.\n-  Fixed a bug (#4905) - :doc:`Loader Library <libraries/loader>` didn't take into account possible user-provided directory paths when loading helpers.\n-  Fixed a bug (#4916) - :doc:`Session Library <libraries/sessions>` with ``sess_match_ip`` enabled was unusable for IPv6 clients when using the 'database' driver on MySQL 5.7.5+.\n-  Fixed a bug (#4917) - :doc:`Date Helper <helpers/date_helper>` function ``nice_date()`` didn't handle YYYYMMDD inputs properly.\n-  Fixed a bug (#4923) - :doc:`Session Library <libraries/sessions>` could execute an erroneous SQL query with the 'database' driver, if the lock attempt times out.\n-  Fixed a bug (#4927) - :doc:`Output Library <libraries/output>` method ``get_header()`` returned the first matching header, regardless of whether it would be replaced by a second ``set_header()`` call.\n-  Fixed a bug (#4844) - :doc:`Email Library <libraries/email>` didn't apply ``escapeshellarg()`` to the while passing the Sendmail ``-f`` parameter through ``popen()``.\n-  Fixed a bug (#4928) - the bootstrap file didn't check if *config/constants.php* exists before trying to load it.\n-  Fixed a bug (#4937) - :doc:`Image Manipulation Library <libraries/image_lib>` method ``initialize()`` didn't translate *new_image* inputs to absolute paths.\n-  Fixed a bug (#4941) - :doc:`Query Builder <database/query_builder>` method ``order_by()`` didn't work with 'RANDOM' under the 'pdo/sqlite' driver.\n-  Fixed a regression (#4892) - :doc:`Query Builder <database/query_builder>` method ``update_batch()`` didn't properly handle identifier escaping.\n-  Fixed a bug (#4953) - :doc:`Database Forge <database/forge>` method ``create_table()`` didn't update an internal tables list cache if it exists but is empty.\n-  Fixed a bug (#4958) - :doc:`Query Builder <database/query_builder>` method ``count_all_results()`` didn't take into account cached ``ORDER BY`` clauses.\n-  Fixed a bug (#4804) - :doc:`Query Builder <database/query_builder>` method ``insert_batch()`` could fail if the input array pointer was modified.\n-  Fixed a bug (#4962) - :doc:`Database Force <database/forge>` method ``alter_table()`` would fail with the 'oci8' driver.\n-  Fixed a bug (#4457) - :doc:`Image Manipulation Library <libraries/image_lib>` method ``get_image_properties()`` didn't detect invalid images.\n-  Fixed a bug (#4765) - :doc:`Email Library <libraries/email>` didn't send the ``User-Agent`` header without a prior call to ``clear()``.\n\nVersion 3.1.2\n=============\n\nRelease Date: Oct 28, 2016\n\n-  **Security**\n\n   -  Fixed a number of new vulnerabilities in :doc:`Security Library <libraries/security>` method ``xss_clean()``.\n\n-  General Changes\n\n   -  Allowed PHP 4-style constructors (``Matching_name::Matching_name()`` methods) to be used as routes, if there's a ``__construct()`` to override them.\n\nBug fixes for 3.1.2\n-------------------\n\n-  Fixed a regression (#4874) - :doc:`Session Library <libraries/sessions>` didn't take into account ``session.hash_bits_per_character`` when validating session IDs.\n-  Fixed a bug (#4871) - :doc:`Query Builder <database/query_builder>` method ``update_batch()`` didn't properly handle identifier escaping.\n-  Fixed a bug (#4884) - :doc:`Query Builder <database/query_builder>` didn't properly parse field names ending in 'is' when used inside WHERE and HAVING statements.\n-  Fixed a bug where ``CI_Log``, ``CI_Output``, ``CI_Email`` and ``CI_Zip`` didn't handle strings in a byte-safe manner when ``mbstring.func_overload`` is enabled.\n\nVersion 3.1.1\n=============\n\nRelease Date: Oct 22, 2016\n\n-  **Security**\n\n   -  Fixed a flaw in :doc:`Security Library <libraries/security>` method ``entity_decode()`` (used by ``xss_clean()``) that affects HTML 5 entities when using PHP 5.3.\n\n-  General Changes\n\n   -  Added ``E_PARSE`` to the list of error levels detected by the shutdown handler.\n   -  Updated :doc:`Inflector Helper <helpers/inflector_helper>` ``is_countable()`` with more words.\n   -  Updated :doc:`common function <general/common_functions>` :php:func:`set_status_header()` with new status codes from IETF RFCs\n      `2817 <https://tools.ietf.org/html/rfc2817>`_ (426)\n      and `6585 <https://tools.ietf.org/html/rfc6585>`_ (428, 429, 431, 511).\n\nBug fixes for 3.1.1\n-------------------\n\n-  Fixed a bug (#4732) - :doc:`Session Library <libraries/sessions>` triggered errors while writing data for a newly-created sessions with the 'memcached' driver.\n-  Fixed a regression (#4736) - :doc:`Image Manipulation Library <libraries/image_lib>` processing via ImageMagick didn't work.\n-  Fixed a bug (#4737) - :doc:`Query Builder <database/query_builder>` didn't add an ``OFFSET`` when ``LIMIT`` is zero or unused.\n-  Fixed a regression (#4739) - :doc:`Email Library <libraries/email>` doesn't properly separate attachment bodies from headers.\n-  Fixed a bug (#4754) - :doc:`Unit Testing Library <libraries/unit_testing>` method ``result()`` didn't translate ``res_datatype``.\n-  Fixed a bug (#4759) - :doc:`Form Validation <libraries/form_validation>`, :doc:`Trackback <libraries/trackback>` and :doc:`XML-RPC <libraries/xmlrpc>` libraries treated URI schemes in a case-sensitive manner.\n-  Fixed a bug (#4762) - :doc:`Cache Library <libraries/caching>` 'file' driver method ``get_metadata()`` checked TTL time against ``mtime`` instead of the cache item's creation time.\n-  Fixed a bug where :doc:`File Uploading Library <libraries/file_uploading>` generated error messages on PHP 7.1.\n-  Fixed a bug (#4780) - :doc:`compatibility function <general/compatibility_functions>` ``hex2bin()`` didn't reject inputs of type \"resource\".\n-  Fixed a bug (#4787) - :doc:`Form Validation Library <libraries/form_validation>` method ``valid_email()`` triggered ``E_WARNING`` when input emails have empty domain names.\n-  Fixed a bug (#4805) - :doc:`Database <database/index>` driver 'mysqli' didn't use the ``MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT`` flag properly.\n-  Fixed a bug (#4808) - :doc:`Database <database/index>` method ``is_write_type()`` only looked at the first line of a queries using ``RETURNING`` with the 'postgre', 'pdo/pgsql', 'odbc' and 'pdo/odbc' drivers.\n-  Fixed a bug where :doc:`Query Builder <database/query_builder>` method ``insert_batch()`` tried to execute an unsupported SQL query with the 'ibase' and 'pdo/firebird' drivers.\n-  Fixed a bug (#4809) - :doc:`Database <database/index>` driver 'pdo/mysql' didn't turn off ``AUTOCOMMIT`` when starting a transaction.\n-  Fixed a bug (#4822) - :doc:`CAPTCHA Helper <helpers/captcha_helper>` didn't clear expired PNG images.\n-  Fixed a bug (#4823) - :doc:`Session Library <libraries/sessions>` 'files' driver could enter an infinite loop if ``mbstring.func_overload`` is enabled.\n-  Fixed a bug (#4851) - :doc:`Database Forge <database/forge>` didn't quote schema names passed to its ``create_database()`` method.\n-  Fixed a bug (#4863) - :doc:`HTML Table Library <libraries/table>` method ``set_caption()`` was missing method chaining support.\n-  Fixed a bug (#4843) - :doc:`XML-RPC Library <libraries/xmlrpc>` client class didn't set a read/write socket timeout.\n-  Fixed a bug (#4865) - uncaught exceptions didn't set the HTTP Response status code to 500 unless ``display_errors`` was turned On.\n-  Fixed a bug (#4830) - :doc:`Session Library <libraries/sessions>` didn't take into account the new session INI settings in PHP 7.1.\n\nVersion 3.1.0\n=============\n\nRelease Date: July 26, 2016\n\n-  **Security**\n\n   -  Fixed an SQL injection in the 'odbc' database driver.\n   -  Updated :php:func:`set_realpath()` :doc:`Path Helper <helpers/path_helper>` function to filter-out ``php://`` wrapper inputs.\n   -  Officially dropped any kind of support for PHP 5.2.x and anything under 5.3.7.\n\n-  General Changes\n\n   -  Updated :doc:`Image Manipulation Library <libraries/image_lib>` to validate *width* and *height* configuration values.\n   -  Updated :doc:`Encryption Library <libraries/encryption>` to always prefer ``random_bytes()`` when it is available.\n   -  Updated :doc:`Session Library <libraries/sessions>` to log 'debug' messages when using fallbacks to *session.save_path* (php.ini) or 'sess_use_database', 'sess_table_name' settings.\n   -  Added a 'LONGTEXT' to 'STRING' alias to :doc:`Database Forge <database/forge>` for the 'cubrid', 'pdo/cubrid' drivers.\n   -  Added 'TINYINT', 'MEDIUMINT', 'INT' and 'BIGINT' aliases to 'NUMBER' to :doc:`Database Forge <database/forge>` for the 'oci8', 'pdo/oci' drivers.\n\n   -  :php:func:`password_hash()` :doc:`compatibility function <general/compatibility_functions>` changes:\n\n      - Changed salt-generation logic to prefer ``random_bytes()`` when it is available.\n      - Changed salt-generation logic to prefer direct access to */dev/urandom* over ``openssl_random_pseudo_bytes()``.\n      - Changed salt-generation logic to error if ``openssl_random_pseudo_bytes()`` sets its ``$crypto_strong`` flag to FALSE.\n\nBug fixes for 3.1.0\n-------------------\n\n-  Fixed a bug where :doc:`Image Manipulation Library <libraries/image_lib>` didn't escape image source paths passed to ImageMagick as shell arguments.\n-  Fixed a bug (#861) - :doc:`Database Forge <database/forge>` method ``create_table()`` incorrectly accepts field width constraints for MSSQL/SQLSRV integer-type columns.\n-  Fixed a bug (#4562) - :doc:`Cache Library <libraries/caching>` didn't check if ``Memcached::quit()`` is available before calling it.\n-  Fixed a bug (#4563) - :doc:`Input Library <libraries/input>` method ``request_headers()`` ignores ``$xss_clean`` parameter value after first call.\n-  Fixed a bug (#4605) - :doc:`Config Library <libraries/config>` method ``site_url()`` stripped trailing slashes from relative URIs passed to it.\n-  Fixed a bug (#4613) - :doc:`Email Library <libraries/email>` failed to send multiple emails via SMTP due to \"already authenticated\" errors when keep-alive is enabled.\n-  Fixed a bug (#4633) - :doc:`Form Validation Library <libraries/form_validation>` ignored multiple \"callback\" rules for empty, non-required fields.\n-  Fixed a bug (#4637) - :doc:`Database <database/index>` method ``error()`` returned ``FALSE`` with the 'oci8' driver if there was no error.\n-  Fixed a bug (#4647) - :doc:`Query Builder <database/query_builder>` method ``count_all_results()`` doesn't take into account ``GROUP BY`` clauses while deciding whether to do a subquery or not.\n-  Fixed a bug where :doc:`Session Library <libraries/sessions>` 'redis' driver didn't properly detect if a connection is properly closed on PHP 5.x.\n-  Fixed a bug (#4583) - :doc:`Email Library <libraries/email>` didn't properly handle inline attachments in HTML emails.\n-  Fixed a bug where :doc:`Database <database/index>` method ``db_select()`` didn't clear metadata cached for the previously used database.\n-  Fixed a bug (#4675) - :doc:`File Helper <helpers/file_helper>` function :php:func:`delete_files()` treated symbolic links as regular directories.\n-  Fixed a bug (#4674) - :doc:`Database <database/index>` driver 'dblib' triggered E_WARNING messages while connecting.\n-  Fixed a bug (#4678) - :doc:`Database Forge <database/forge>` tried to use unsupported ``IF NOT EXISTS`` clause when creating tables on Oracle.\n-  Fixed a bug (#4691) - :doc:`File Uploading Library <libraries/file_uploading>` method ``data()`` returns wrong 'raw_name' when the filename extension is also contained in the raw filename.\n-  Fixed a bug (#4679) - :doc:`Input Library <libraries/input>` method ``ip_address()`` errors with a matching ``$config['proxy_ips']`` IPv6 address.\n-  Fixed a bug (#4695) - :doc:`User Agent Library <libraries/user_agent>` didn't load the *config/user_agents.php* file when there's no ``User-Agent`` HTTP request header.\n-  Fixed a bug (#4713) - :doc:`Query Builder <database/query_builder>` methods ``insert_batch()``, ``update_batch()`` could return wrong affected rows count.\n-  Fixed a bug (#4712) - :doc:`Email Library <libraries/email>` doesn't sent ``RSET`` to SMTP servers after a failure and while using keep-alive.\n-  Fixed a bug (#4724) - :doc:`Common function <general/common_functions>` :php:func:`is_https()` compared the ``X-Forwarded-Proto`` HTTP header case-sensitively.\n-  Fixed a bug (#4725) - :doc:`Common function <general/common_functions>` :php:func:`remove_invisible_characters()` searched case-sensitively for URL-encoded characters.\n\nVersion 3.0.6\n=============\n\nRelease Date: March 21, 2016\n\n-  General Changes\n\n   -  Added a destructor to :doc:`Cache Library <libraries/caching>` 'memcached' driver to ensure that Memcache(d) connections are properly closed.\n   -  Deprecated :doc:`Form Validation Library <libraries/form_validation>` method ``prep_for_form()``.\n\nBug fixes for 3.0.6\n-------------------\n\n-  Fixed a bug (#4516) - :doc:`Form Validation Library <libraries/form_validation>` always accepted empty array inputs.\n-  Fixed a bug where :doc:`Session Library <libraries/sessions>` allowed accessing ``$_SESSION`` values as class properties but ``isset()`` didn't work on them.\n-  Fixed a bug where :doc:`Form Validation Library <libraries/form_validation>` modified the ``$_POST`` array when the data being validated was actually provided via ``set_data()``.\n-  Fixed a bug (#4539) - :doc:`Migration Library <libraries/migration>` applied migrations before validating that all migrations within the requested version range are valid.\n-  Fixed a bug (#4539) - :doc:`Migration Library <libraries/migration>` triggered failures for migrations that are out of the requested version range.\n\nVersion 3.0.5\n=============\n\nRelease Date: March 11, 2016\n\n-  Core\n\n   -  Changed :doc:`Loader Library <libraries/loader>` to allow ``$autoload['drivers']`` assigning with custom property names.\n   -  Changed :doc:`Loader Library <libraries/loader>` to ignore variables prefixed with '_ci_' when loading views.\n\n-  General Changes\n\n   -  Updated the :doc:`Session Library <libraries/sessions>` to produce friendlier error messages on failures with drivers other than 'files'.\n\n-  :doc:`Query Builder <database/query_builder>`\n\n   -  Added a ``$batch_size`` parameter to the ``insert_batch()`` method (defaults to 100).\n   -  Added a ``$batch_size`` parameter to the ``update_batch()`` method (defaults to 100).\n\nBug fixes for 3.0.5\n-------------------\n\n-  Fixed a bug (#4391) - :doc:`Email Library <libraries/email>` method ``reply_to()`` didn't apply Q-encoding.\n-  Fixed a bug (#4384) - :doc:`Pagination Library <libraries/pagination>` ignored (possible) *cur_page* configuration value.\n-  Fixed a bug (#4395) - :doc:`Query Builder <database/query_builder>` method ``count_all_results()`` still fails if an ``ORDER BY`` condition is used.\n-  Fixed a bug (#4399) - :doc:`Query Builder <database/query_builder>` methods ``insert_batch()``, ``update_batch()`` produced confusing error messages when called with no data and *db_debug* is enabled.\n-  Fixed a bug (#4401) - :doc:`Query Builder <database/query_builder>` breaks ``WHERE`` and ``HAVING`` conditions that use ``IN()`` with strings containing a closing parenthesis.\n-  Fixed a regression in :doc:`Form Helper <helpers/form_helper>` functions :php:func:`set_checkbox()`, :php:func:`set_radio()` where \"checked\" inputs aren't recognized after a form submit.\n-  Fixed a bug (#4407) - :doc:`Text Helper <helpers/text_helper>` function :php:func:`word_censor()` doesn't work under PHP 7 if there's no custom replacement provided.\n-  Fixed a bug (#4415) - :doc:`Form Validation Library <libraries/form_validation>` rule **valid_url** didn't accept URLs with IPv6 addresses enclosed in square brackets under PHP 5 (upstream bug).\n-  Fixed a bug (#4427) - :doc:`CAPTCHA Helper <helpers/captcha_helper>` triggers an error if the provided character pool is too small.\n-  Fixed a bug (#4430) - :doc:`File Uploading Library <libraries/file_uploading>` option **file_ext_tolower** didn't work.\n-  Fixed a bug (#4431) - :doc:`Query Builder <database/query_builder>` method ``join()`` discarded opening parentheses.\n-  Fixed a bug (#4424) - :doc:`Session Library <libraries/sessions>` triggered a PHP warning when writing a newly created session with the 'redis' driver.\n-  Fixed a bug (#4437) - :doc:`Inflector Helper <helpers/inflector_helper>` function :php:func:`humanize()` didn't escape its ``$separator`` parameter while using it in a regular expression.\n-  Fixed a bug where :doc:`Session Library <libraries/sessions>` didn't properly handle its locks' statuses with the 'memcached' driver.\n-  Fixed a bug where :doc:`Session Library <libraries/sessions>` triggered a PHP warning when writing a newly created session with the 'memcached' driver.\n-  Fixed a bug (#4449) - :doc:`Query Builder <database/query_builder>` method ``join()`` breaks conditions containing ``IS NULL``, ``IS NOT NULL``.\n-  Fixed a bug (#4491) - :doc:`Session Library <libraries/sessions>` didn't clean-up internal variables for emulated locks with the 'redis' driver.\n-  Fixed a bug where :doc:`Session Library <libraries/sessions>` didn't clean-up internal variables for emulated locks with the 'memcached' driver.\n-  Fixed a bug where :doc:`Database <database/index>` transactions didn't work with the 'ibase' driver.\n-  Fixed a bug (#4475) - :doc:`Security Library <libraries/security>` method ``strip_image_tags()`` preserves only the first URL character from non-quoted *src* attributes.\n-  Fixed a bug where :doc:`Profiler Library <general/profiling>` didn't apply ``htmlspecialchars()`` to all displayed inputs.\n-  Fixed a bug (#4277) - :doc:`Cache Library <libraries/caching>` triggered fatal errors if accessing the Memcache(d) and/or Redis driver and they are not available on the system.\n-  Fixed a bug where :doc:`Cache Library <libraries/caching>` method ``is_supported()`` logged an error message when it returns ``FALSE`` for the APC and Wincache drivers.\n\nVersion 3.0.4\n=============\n\nRelease Date: January 13, 2016\n\n-  General Changes\n\n   -  Updated :doc:`Security Library <libraries/security>` method ``get_random_bytes()`` to use PHP 7's ``random_bytes()`` function when possible.\n   -  Updated :doc:`Encryption Library <libraries/security>` method ``create_key()`` to use PHP 7's ``random_bytes()`` function when possible.\n\n-  :doc:`Database <database/index>`\n\n   -  Added support for ``OFFSET-FETCH`` with Oracle 12c for the 'oci8' and 'pdo/oci' drivers.\n   -  Added support for the new ``MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT`` constant from `PHP 5.6.16 <https://secure.php.net/ChangeLog-5.php#5.6.16>`_ for the 'mysqli' driver.\n\nBug fixes for 3.0.4\n-------------------\n\n-  Fixed a bug (#4212) - :doc:`Query Builder <database/query_builder>` method ``count_all_results()`` could fail if an ``ORDER BY`` condition is used.\n-  Fixed a bug where :doc:`Form Helper <helpers/form_helper>` functions :php:func:`set_checkbox()`, :php:func:`set_radio()` didn't \"uncheck\" inputs on a submitted form if the default state is \"checked\".\n-  Fixed a bug (#4217) - :doc:`Config Library <libraries/config>` method ``base_url()`` didn't use proper formatting for IPv6 when it falls back to ``$_SERVER['SERVER_ADDR']``.\n-  Fixed a bug where :doc:`CAPTCHA Helper <helpers/captcha_helper>` entered an infinite loop while generating a random string.\n-  Fixed a bug (#4223) - :doc:`Database <database/index>` method ``simple_query()`` blindly executes queries without checking if the connection was initialized properly.\n-  Fixed a bug (#4244) - :doc:`Email Library <libraries/email>` could improperly use \"unsafe\" US-ASCII characters during Quoted-printable encoding.\n-  Fixed a bug (#4245) - :doc:`Database Forge <database/forge>` couldn't properly handle ``SET`` and ``ENUM`` type fields with string values.\n-  Fixed a bug (#4283) - :doc:`String Helper <helpers/string_helper>` function :php:func:`alternator()` couldn't be called without arguments.\n-  Fixed a bug (#4306) - :doc:`Database <database/index>` method ``version()`` didn't work properly with the 'mssql' driver.\n-  Fixed a bug (#4039) - :doc:`Session Library <libraries/sessions>` could generate multiple (redundant) warnings in case of a read failure with the 'files' driver, due to a bug in PHP.\n-  Fixed a bug where :doc:`Session Library <libraries/sessions>` didn't have proper error handling on PHP 5 (due to a PHP bug).\n-  Fixed a bug (#4312) - :doc:`Form Validation Library <libraries/form_validation>` didn't provide error feedback for failed validation on empty requests.\n-  Fixed a bug where :doc:`Database <database/index>` method `version()` returned banner text instead of only the version number with the 'oci8' and 'pdo/oci' drivers.\n-  Fixed a bug (#4331) - :doc:`Database <database/index>` method ``error()`` didn't really work for connection errors with the 'mysqli' driver.\n-  Fixed a bug (#4343) - :doc:`Email Library <libraries/email>` failing with a *\"More than one 'from' person\"* message when using *sendmail*.\n-  Fixed a bug (#4350) - :doc:`Loader Library <libraries/loader>` method ``model()`` logic directly instantiated the ``CI_Model`` or ``MY_Model`` classes.\n-  Fixed a bug (#4337) - :doc:`Database <database/index>` method ``query()`` didn't return a result set for queries with the ``RETURNING`` statement on PostgreSQL.\n-  Fixed a bug (#4362) - :doc:`Session Library <libraries/sessions>` doesn't properly maintain its state after ID regeneration with the 'redis' and 'memcached' drivers on PHP 7.\n-  Fixed a bug (#4349) - :doc:`Database <database/index>` drivers 'mysql', 'mysqli', 'pdo/mysql' discard other ``sql_mode`` flags when \"stricton\" is enabled.\n-  Fixed a bug (#4349) - :doc:`Database <database/index>` drivers 'mysql', 'mysqli', 'pdo/mysql' don't turn off ``STRICT_TRANS_TABLES`` on MySQL 5.7+ when \"stricton\" is disabled.\n-  Fixed a bug (#4374) - :doc:`Session Library <libraries/sessions>` with the 'database' driver could be affected by userspace :doc:`Query Builder <database/query_builder>` conditions.\n\nVersion 3.0.3\n=============\n\nRelease Date: October 31, 2015\n\n-  **Security**\n\n   -  Fixed an XSS attack vector in :doc:`Security Library <libraries/security>` method ``xss_clean()``.\n   -  Changed :doc:`Config Library <libraries/config>` method ``base_url()`` to fallback to ``$_SERVER['SERVER_ADDR']`` when ``$config['base_url']`` is empty in order to avoid *Host* header injections.\n   -  Changed :doc:`CAPTCHA Helper <helpers/captcha_helper>` to use the operating system's PRNG when possible.\n\n-  :doc:`Database <database/index>`\n\n   -  Optimized :doc:`Database Utility <database/utilities>` method ``csv_from_result()`` for speed with larger result sets.\n   -  Added proper return values to :doc:`Database Transactions <database/transactions>` method ``trans_start()``.\n\nBug fixes for 3.0.3\n-------------------\n\n-  Fixed a bug (#4170) - :doc:`Database <database/index>` method ``insert_id()`` could return an identity from the wrong scope with the 'sqlsrv' driver.\n-  Fixed a bug (#4179) - :doc:`Session Library <libraries/sessions>` doesn't properly maintain its state after ID regeneration with the 'database' driver on PHP 7.\n-  Fixed a bug (#4173) - :doc:`Database Forge <database/forge>` method ``add_key()`` didn't allow creation of non-PRIMARY composite keys after the \"bugfix\" for #3968.\n-  Fixed a bug (#4171) - :doc:`Database Transactions <database/transactions>` didn't work with nesting in methods ``trans_begin()``, ``trans_commit()``, ``trans_rollback()``.\n-  Fixed a bug where :doc:`Database Transaction <database/transactions>` methods ``trans_begin()``, ``trans_commit()``, ``trans_rollback()`` ignored failures.\n-  Fixed a bug where all :doc:`Database Transaction <database/transactions>` methods returned TRUE while transactions are actually disabled.\n-  Fixed a bug where :doc:`common function <general/common_functions>` :php:func:`html_escape()` modified keys of its array inputs.\n-  Fixed a bug (#4192) - :doc:`Email Library <libraries/email>` wouldn't always have proper Quoted-printable encoding due to a bug in PHP's own ``mb_mime_encodeheader()`` function.\n\nVersion 3.0.2\n=============\n\nRelease Date: October 8, 2015\n\n-  **Security**\n\n   -  Fixed a number of XSS attack vectors in :doc:`Security Library <libraries/security>` method ``xss_clean()``  (thanks to Frans Rosén from `Detectify <https://detectify.com/>`_).\n\n-  General Changes\n\n   -  Updated the *application/config/constants.php* file to check if constants aren't already defined before doing that.\n   -  Changed :doc:`Loader Library <libraries/loader>` method ``model()`` to only apply ``ucfirst()`` and not ``strtolower()`` to the requested class name.\n   -  Changed :doc:`Config Library <libraries/config>` methods ``base_url()``, ``site_url()`` to allow protocol-relative URLs by passing an empty string as the protocol.\n\nBug fixes for 3.0.2\n-------------------\n\n-  Fixed a bug (#2284) - :doc:`Database <database/index>` method ``protect_identifiers()`` breaks when :doc:`Query Builder <database/query_builder>` isn't enabled.\n-  Fixed a bug (#4052) - :doc:`Routing <general/routing>` with anonymous functions didn't work for routes that don't use regular expressions.\n-  Fixed a bug (#4056) - :doc:`Input Library <libraries/input>` method ``get_request_header()`` could not return a value unless ``request_headers()`` was called beforehand.\n-  Fixed a bug where the :doc:`Database Class <database/index>` entered an endless loop if it fails to connect with the 'sqlsrv' driver.\n-  Fixed a bug (#4065) - :doc:`Database <database/index>` method ``protect_identifiers()`` treats a traling space as an alias separator if the input doesn't contain ' AS '.\n-  Fixed a bug (#4066) - :doc:`Cache Library <libraries/caching>` couldn't fallback to a backup driver if the primary one is Memcache(d) or Redis.\n-  Fixed a bug (#4073) - :doc:`Email Library <libraries/email>` method ``send()`` could return TRUE in case of an actual failure when an SMTP command fails.\n-  Fixed a bug (#4086) - :doc:`Query Builder <database/query_builder>` didn't apply *dbprefix* to LIKE conditions if the pattern included spaces.\n-  Fixed a bug (#4091) - :doc:`Cache Library <libraries/caching>` 'file' driver could be tricked into accepting empty cache item IDs.\n-  Fixed a bug (#4093) - :doc:`Query Builder <database/query_builder>` modified string values containing 'AND', 'OR' while compiling WHERE conditions.\n-  Fixed a bug (#4096) - :doc:`Query Builder <database/query_builder>` didn't apply *dbprefix* when compiling BETWEEN conditions.\n-  Fixed a bug (#4105) - :doc:`Form Validation Library <libraries/form_validation>` didn't allow pipe characters inside \"bracket parameters\" when using a string ruleset.\n-  Fixed a bug (#4109) - :doc:`Routing <general/routing>` to *default_controller* didn't work when *enable_query_strings* is set to TRUE.\n-  Fixed a bug (#4044) - :doc:`Cache Library <libraries/caching>` 'redis' driver didn't catch ``RedisException`` that could be thrown during authentication.\n-  Fixed a bug (#4120) - :doc:`Database <database/index>` method ``error()`` didn't return error info when called after ``query()`` with the 'mssql' driver.\n-  Fixed a bug (#4116) - :doc:`Pagination Library <libraries/pagination>` set the wrong page number on the \"data-ci-pagination-page\" attribute in generated links.\n-  Fixed a bug where :doc:`Pagination Library <libraries/pagination>` added the 'rel=\"start\"' attribute to the first displayed link even if it's not actually linking the first page.\n-  Fixed a bug (#4137) - :doc:`Error Handling <general/errors>` breaks for the new ``Error`` exceptions under PHP 7.\n-  Fixed a bug (#4126) - :doc:`Form Validation Library <libraries/form_validation>` method ``reset_validation()`` discarded validation rules from config files.\n\nVersion 3.0.1\n=============\n\nRelease Date: August 7, 2015\n\n-  Core\n\n   -  Added DoS mitigation to :php:func:`hash_pbkdf2()` :doc:`compatibility function <general/compatibility_functions>`.\n\n-  Database\n\n   -  Added ``list_fields()`` support for SQLite ('sqlite3' and 'pdo_sqlite' drivers).\n   -  Added SSL connection support for the 'mysqli' and 'pdo_mysql' drivers.\n\n-  Libraries\n\n   -  :doc:`File Uploading Library <libraries/file_uploading>` changes:\n\n      - Changed method ``set_error()`` to accept a custom log level (defaults to 'error').\n      - Errors \"no_file_selected\", \"file_partial\", \"stopped_by_extension\", \"no_file_types\", \"invalid_filetype\", \"bad_filename\" are now logged at the 'debug' level.\n      - Errors \"file_exceeds_limit\", \"file_exceeds_form_limit\", \"invalid_filesize\", \"invalid_dimensions\" are now logged at the 'info' level.\n\n   -  Added 'is_resource' to the available expectations in :doc:`Unit Testing Library <libraries/unit_testing>`.\n\n-  Helpers\n\n   -  Added Unicode support to :doc:`URL Helper <helpers/url_helper>` function :php:func:`url_title()`.\n   -  Added support for passing the \"extra\" parameter as an array to all :doc:`Form Helper <helpers/form_helper>` functions that use it.\n\n-  Core\n\n   -  Added support for defining a list of specific query parameters in ``$config['cache_query_string']`` for the :doc:`Output Library <libraries/output>`.\n   -  Added class existence and inheritance checks to ``CI_Loader::model()`` in order to ease debugging in case of name collisions.\n\nBug fixes for 3.0.1\n-------------------\n\n-  Fixed a bug (#3733) - Autoloading of libraries with aliases didn't work, although it was advertised to.\n-  Fixed a bug (#3744) - Redis :doc:`Caching <libraries/caching>` driver didn't handle authentication failures properly.\n-  Fixed a bug (#3761) - :doc:`URL Helper <helpers/url_helper>` function :php:func:`anchor()` didn't work with array inputs.\n-  Fixed a bug (#3773) - ``db_select()`` didn't work for MySQL with the PDO :doc:`Database <database/index>` driver.\n-  Fixed a bug (#3771) - :doc:`Form Validation Library <libraries/form_validation>` was looking for a 'form_validation\\_' prefix when trying to translate field name labels.\n-  Fixed a bug (#3787) - :doc:`FTP Library <libraries/ftp>` method ``delete_dir()`` failed when the target has subdirectories.\n-  Fixed a bug (#3801) - :doc:`Output Library <libraries/output>` method ``_display_cache()`` incorrectly looked for the last modified time of a directory instead of the cache file.\n-  Fixed a bug (#3816) - :doc:`Form Validation Library <libraries/form_validation>` treated empty string values as non-existing ones.\n-  Fixed a bug (#3823) - :doc:`Session Library <libraries/sessions>` drivers Redis and Memcached didn't properly handle locks that are blocking the request for more than 30 seconds.\n-  Fixed a bug (#3846) - :doc:`Image Manipulation Library <libraries/image_lib>` method `image_mirror_gd()` didn't properly initialize its variables.\n-  Fixed a bug (#3854) - `field_data()` didn't work properly with the Oracle (OCI8) database driver.\n-  Fixed a bug in the :doc:`Database Utility Class <database/utilities>` method ``csv_from_result()`` didn't work with a whitespace CSV delimiter.\n-  Fixed a bug (#3890) - :doc:`Input Library <libraries/input>` method ``get_request_header()`` treated header names as case-sensitive.\n-  Fixed a bug (#3903) - :doc:`Form Validation Library <libraries/form_validation>` ignored \"unnamed\" closure validation rules.\n-  Fixed a bug (#3904) - :doc:`Form Validation Library <libraries/form_validation>` ignored \"named\" callback rules when the field is empty and there's no 'required' rule.\n-  Fixed a bug (#3922) - :doc:`Email <libraries/email>` and :doc:`XML-RPC <libraries/xmlrpc>` libraries could enter an infinite loop due to `PHP bug #39598 <https://bugs.php.net/bug.php?id=39598>`_.\n-  Fixed a bug (#3913) - :doc:`Cache Library <libraries/caching>` didn't work with the direct ``$this->cache->$driver_name->method()`` syntax with Redis and Memcache(d).\n-  Fixed a bug (#3932) - :doc:`Query Builder <database/query_builder>` didn't properly compile WHERE and HAVING conditions for field names that end with \"and\", \"or\".\n-  Fixed a bug in :doc:`Query Builder <database/query_builder>` where ``delete()`` didn't properly work on multiple tables with a WHERE condition previously set via ``where()``.\n-  Fixed a bug (#3952) - :doc:`Database <database/index>` method ``list_fields()`` didn't work with SQLite3.\n-  Fixed a bug (#3955) - :doc:`Cache Library <libraries/caching>` methods ``increment()`` and ``decrement()`` ignored the 'key_prefix' setting.\n-  Fixed a bug (#3963) - :doc:`Unit Testing Library <libraries/unit_testing>` wrongly tried to translate filenames, line numbers and notes values in test results.\n-  Fixed a bug (#3965) - :doc:`File Uploading Library <libraries/file_uploading>` ignored the \"encrypt_name\" setting when \"overwrite\" is enabled.\n-  Fixed a bug (#3968) - :doc:`Database Forge <database/forge>` method ``add_key()`` didn't treat array inputs as composite keys unless it's a PRIMARY KEY.\n-  Fixed a bug (#3715) - :doc:`Pagination Library <libraries/pagination>` could generate broken link when a protocol-relative base URL is used.\n-  Fixed a bug (#3828) - :doc:`Output Library <libraries/output>` method ``delete_cache()`` couldn't delete index page caches.\n-  Fixed a bug (#3704) - :doc:`Database <database/index>` method ``stored_procedure()`` in the 'oci8' driver didn't properly bind parameters.\n-  Fixed a bug (#3778) - :doc:`Download Helper <helpers/download_helper>` function :php:func:`force_download()` incorrectly sent a *Pragma* response header.\n-  Fixed a bug (#3752) - ``$routing['directory']`` overrides were not properly handled and always resulted in a 404 \"Not Found\" error.\n-  Fixed a bug (#3279) - :doc:`Query Builder <database/query_builder>` methods ``update()`` and ``get_compiled_update()`` did double escaping on the table name if it was provided via ``from()``.\n-  Fixed a bug (#3991) - ``$config['rewrite_short_tags']`` never worked due to ``function_exists('eval')`` always returning FALSE.\n-  Fixed a bug where the :doc:`File Uploading Library <libraries/file_uploading>` library will not properly configure its maximum file size unless the input value is of type integer.\n-  Fixed a bug (#4000) - :doc:`Pagination Library <libraries/pagination>` didn't enable \"rel\" attributes by default if no attributes-related config options were used.\n-  Fixed a bug (#4004) - :doc:`URI Class <libraries/uri>` didn't properly parse the request URI if it contains a colon followed by a digit.\n-  Fixed a bug in :doc:`Query Builder <database/query_builder>` where the ``$escape`` parameter for some methods only affected field names.\n-  Fixed a bug (#4012) - :doc:`Query Builder <database/query_builder>` methods ``where_in()``, ``or_where_in()``, ``where_not_in()``, ``or_where_not_in()`` didn't take into account previously cached WHERE conditions when query cache is in use.\n-  Fixed a bug (#4015) - :doc:`Email Library <libraries/email>` method ``set_header()`` didn't support method chaining, although it was advertised.\n-  Fixed a bug (#4027) - :doc:`Routing <general/routing>` with HTTP verbs only worked if the route request method was declared in all-lowercase letters.\n-  Fixed a bug (#4026) - :doc:`Database Transactions <database/transactions>` always rollback if any previous ``query()`` call fails.\n-  Fixed a bug (#4023) - :doc:`String Helper <helpers/string_helper>` function ``increment_string()`` didn't escape its ``$separator`` parameter.\n\nVersion 3.0.0\n=============\n\nRelease Date: March 30, 2015\n\n-  License\n\n   -  CodeIgniter has been relicensed with the `MIT License <https://opensource.org/licenses/MIT>`_, eliminating its old proprietary licensing.\n\n-  General Changes\n\n   -  PHP 5.1.6 is no longer supported. CodeIgniter now requires PHP 5.2.4 and recommends PHP 5.4+ or newer to be used.\n   -  Changed filenaming convention (class file names now must be Ucfirst and everything else in lowercase).\n   -  Changed the default database driver to 'mysqli' (the old 'mysql' driver is DEPRECATED).\n   -  ``$_SERVER['CI_ENV']`` can now be set to control the ``ENVIRONMENT`` constant.\n   -  Added an optional backtrace to php-error template.\n   -  Added Android to the list of user agents.\n   -  Added Windows 7, Windows 8, Windows 8.1, Android, Blackberry, iOS and PlayStation 3 to the list of user platforms.\n   -  Added Fennec (Firefox for mobile) to the list of mobile user agents.\n   -  Ability to log certain error types, not all under a threshold.\n   -  Added support for pem, p10, p12, p7a, p7c, p7m, p7r, p7s, crt, crl, der, kdb, rsa, cer, sst, csr Certs to mimes.php.\n   -  Added support for pgp, gpg, zsh and cdr files to mimes.php.\n   -  Added support for 3gp, 3g2, mp4, wmv, f4v, vlc Video files to mimes.php.\n   -  Added support for m4a, aac, m4u, xspf, au, ac3, flac, ogg, wma Audio files to mimes.php.\n   -  Added support for kmz and kml (Google Earth) files to mimes.php.\n   -  Added support for ics Calendar files to mimes.php.\n   -  Added support for rar, jar and 7zip archives to mimes.php.\n   -  Updated support for xml ('application/xml') and xsl ('application/xml', 'text/xsl') files in mimes.php.\n   -  Updated support for doc files in mimes.php.\n   -  Updated support for docx files in mimes.php.\n   -  Updated support for php files in mimes.php.\n   -  Updated support for zip files in mimes.php.\n   -  Updated support for csv files in mimes.php.\n   -  Added Romanian, Greek, Vietnamese and Cyrilic characters in *application/config/foreign_characters.php*.\n   -  Changed logger to only chmod when file is first created.\n   -  Removed previously deprecated SHA1 Library.\n   -  Removed previously deprecated use of ``$autoload['core']`` in *application/config/autoload.php*.\n      Only entries in ``$autoload['libraries']`` are auto-loaded now.\n   -  Removed previously deprecated EXT constant.\n   -  Updated all classes to be written in PHP 5 style, with visibility declarations and no ``var`` usage for properties.\n   -  Added an Exception handler.\n   -  Moved error templates to *application/views/errors/* and made the path configurable via ``$config['error_views_path']``.\n   -  Added support non-HTML error templates for CLI applications.\n   -  Moved the Log class to *application/core/*\n   -  Global config files are loaded first, then environment ones. Environment config keys overwrite base ones, allowing to only set the keys we want changed per environment.\n   -  Changed detection of ``$view_folder`` so that if it's not found in the current path, it will now also be searched for under the application folder.\n   -  Path constants BASEPATH, APPPATH and VIEWPATH are now (internally) defined as absolute paths.\n   -  Updated email validation methods to use ``filter_var()`` instead of PCRE.\n   -  Changed environment defaults to report all errors in *development* and only fatal ones in *testing*, *production* but only display them in *development*.\n   -  Updated *ip_address* database field lengths from 16 to 45 for supporting IPv6 address on :doc:`Trackback Library <libraries/trackback>` and :doc:`Captcha Helper <helpers/captcha_helper>`.\n   -  Removed *cheatsheets* and *quick_reference* PDFs from the documentation.\n   -  Added availability checks where usage of dangerous functions like ``eval()`` and ``exec()`` is required.\n   -  Added support for changing the file extension of log files using ``$config['log_file_extension']``.\n   -  Added support for turning newline standardization on/off via ``$config['standardize_newlines']`` and set it to FALSE by default.\n   -  Added configuration setting ``$config['composer_autoload']`` to enable loading of a `Composer <https://getcomposer.org>`_ auto-loader.\n   -  Removed the automatic conversion of 'programmatic characters' to HTML entities from the :doc:`URI Library <libraries/uri>`.\n   -  Changed log messages that say a class or file was loaded to \"info\" level instead of \"debug\", so that they don't pollute log files when ``$config['log_threshold']`` is set to 2 (debug).\n\n-  Helpers\n\n   -  :doc:`Date Helper <helpers/date_helper>` changes include:\n\n      - Added an optional third parameter to :php:func:`timespan()` that constrains the number of time units displayed.\n      - Added an optional parameter to :php:func:`timezone_menu()` that allows more attributes to be added to the generated select tag.\n      - Added function :php:func:`date_range()` that generates a list of dates between a specified period.\n      - Deprecated ``standard_date()``, which now just uses the native ``date()`` with `DateTime constants <https://secure.php.net/manual/en/class.datetime.php#datetime.constants.types>`_.\n      - Changed :php:func:`now()` to work with all timezone strings supported by PHP.\n      - Changed :php:func:`days_in_month()` to use the native ``cal_days_in_month()`` PHP function, if available.\n\n   -  :doc:`URL Helper <helpers/url_helper>` changes include:\n\n      - Deprecated *separator* options **dash** and **underscore** for function :php:func:`url_title()` (they are only aliases for '-' and '_' respectively).\n      - :php:func:`url_title()` will now trim extra dashes from beginning and end.\n      - :php:func:`anchor_popup()` will now fill the *href* attribute with the URL and its JS code will return FALSE instead.\n      - Added JS window name support to the :php:func:`anchor_popup()` function.\n      - Added support for menubar attribute to the :php:func:`anchor_popup()`.\n      - Added support (auto-detection) for HTTP/1.1 response codes 303, 307 in :php:func:`redirect()`.\n      - Changed :php:func:`redirect()` to choose the **refresh** method only on IIS servers, instead of all servers on Windows (when **auto** is used).\n      - Changed :php:func:`anchor()`, :php:func:`anchor_popup()`, and :php:func:`redirect()` to support protocol-relative URLs (e.g. *//ellislab.com/codeigniter*).\n\n   -  :doc:`HTML Helper <helpers/html_helper>` changes include:\n\n      - Added more doctypes.\n      - Changed application and environment config files to be loaded in a cascade-like manner.\n      - Changed :php:func:`doctype()` to cache and only load once the doctypes array.\n      - Deprecated functions ``nbs()`` and ``br()``, which are just aliases for the native ``str_repeat()`` with ``&nbsp;`` and ``<br />`` respectively.\n\n   -  :doc:`Inflector Helper <helpers/inflector_helper>` changes include:\n\n      - Changed :php:func:`humanize()` to allow passing an input separator as its second parameter.\n      - Changed :php:func:`humanize()` and :php:func:`underscore()` to utilize `mbstring <https://secure.php.net/mbstring>`_, if available.\n      - Changed :php:func:`plural()` and :php:func:`singular()` to avoid double pluralization and support more words.\n\n   -  :doc:`Download Helper <helpers/download_helper>` changes include:\n\n      - Added an optional third parameter to :php:func:`force_download()` that enables/disables sending the actual file MIME type in the Content-Type header (disabled by default).\n      - Added a work-around in :php:func:`force_download()` for a bug Android <= 2.1, where the filename extension needs to be in uppercase.\n      - Added support for reading from an existing file path by passing NULL as the second parameter to :php:func:`force_download()` (useful for large files and/or safely transmitting binary data).\n\n   -  :doc:`Form Helper <helpers/form_helper>` changes include:\n\n      - :php:func:`form_dropdown()` will now also take an array for unity with other form helpers.\n      - ``form_prep()`` is now DEPRECATED and only acts as an alias for :doc:`common function <general/common_functions>` :php:func:`html_escape()`.\n      - :php:func:`set_value()` will now also accept a third argument, allowing to turn off HTML escaping of the value.\n\n   -  :doc:`Security Helper <helpers/security_helper>` changes include:\n\n      - ``do_hash()`` now uses PHP's native ``hash()`` function (supporting more algorithms) and is deprecated.\n      - :php:func:`strip_image_tags()` is now an alias for the same method in the :doc:`Security Library <libraries/security>`.\n\n   -  *Smiley Helper* changes include:\n\n      - Deprecated the whole helper as too specific for CodeIgniter.\n      - Removed previously deprecated function ``js_insert_smiley()``.\n      - Changed application and environment config files to be loaded in a cascade-like manner.\n      - The smileys array is now cached and loaded only once.\n\n   -  :doc:`File Helper <helpers/file_helper>` changes include:\n\n      - :php:func:`set_realpath()` can now also handle file paths as opposed to just directories.\n      - Added an optional paramater to :php:func:`delete_files()` to enable it to skip deleting files such as *.htaccess* and *index.html*.\n      - Deprecated function ``read_file()`` - it's just an alias for PHP's native ``file_get_contents()``.\n\n   -  :doc:`String Helper <helpers/string_helper>` changes include:\n\n      - Deprecated function ``repeater()`` - it's just an alias for PHP's native ``str_repeat()``.\n      - Deprecated function ``trim_slashes()`` - it's just an alias for PHP's native ``trim()`` (with a slash as its second argument).\n      - Deprecated randomization type options **unique** and **encrypt** for funcion :php:func:`random_string()` (they are only aliases for **md5** and **sha1** respectively).\n\n   -  :doc:`CAPTCHA Helper <helpers/captcha_helper>` changes include:\n\n      - Added *word_length* and *pool* options to allow customization of the generated word.\n      - Added *colors* configuration to allow customization for the *background*, *border*, *text* and *grid* colors.\n      - Added *filename* to the returned array elements.\n      - Updated to use `imagepng()` in case that `imagejpeg()` isn't available.\n      - Added **font_size** option to allow customization of font size.\n      - Added **img_id** option to set id attribute of captcha image.\n\n   -  :doc:`Text Helper <helpers/text_helper>` changes include:\n\n      - Changed the default tag for use in :php:func:`highlight_phrase()` to ``<mark>`` (formerly ``<strong>``).\n      - Changed :php:func:`character_limiter()`, :php:func:`word_wrap()` and :php:func:`ellipsize()` to utilize `mbstring <https://secure.php.net/mbstring>`_ or `iconv <https://secure.php.net/iconv>`_, if available.\n\n   -  :doc:`Directory Helper <helpers/directory_helper>` :php:func:`directory_map()` will now append ``DIRECTORY_SEPARATOR`` to directory names in the returned array.\n   -  :doc:`Array Helper <helpers/array_helper>` :php:func:`element()` and :php:func:`elements()` now return NULL instead of FALSE when the required elements don't exist.\n   -  :doc:`Language Helper <helpers/language_helper>` :php:func:`lang()` now accepts an optional list of additional HTML attributes.\n   -  Deprecated the *Email Helper* as its ``valid_email()``, ``send_email()`` functions are now only aliases for PHP native functions ``filter_var()`` and ``mail()`` respectively.\n\n-  Database\n\n   -  DEPRECATED the 'mysql', 'sqlite', 'mssql' and 'pdo/dblib' (also known as 'pdo/mssql' or 'pdo/sybase') drivers.\n   -  Added **dsn** configuration setting for drivers that support DSN strings (PDO, PostgreSQL, Oracle, ODBC, CUBRID).\n   -  Added **schema** configuration setting (defaults to *public*) for drivers that might need it (currently used by PostgreSQL and ODBC).\n   -  Added **save_queries** configuration setting to *application/config/database.php* (defaults to ``TRUE``).\n   -  Removed **autoinit** configuration setting as it doesn't make sense to instantiate the database class but not connect to the database.\n   -  Added subdrivers support (currently only used by PDO).\n   -  Added an optional database name parameter to ``db_select()``.\n   -  Removed ``protect_identifiers()`` and renamed internal method ``_protect_identifiers()`` to it instead - it was just an alias.\n   -  Renamed internal method ``_escape_identifiers()`` to ``escape_identifiers()``.\n   -  Updated ``escape_identifiers()`` to accept an array of fields as well as strings.\n   -  MySQL and MySQLi drivers now require at least MySQL version 5.1.\n   -  Added a ``$persistent`` parameter to ``db_connect()`` and changed ``db_pconnect()`` to be an alias for ``db_connect(TRUE)``.\n   -  ``db_set_charset()`` now only requires one parameter (collation was only needed due to legacy support for MySQL versions prior to 5.1).\n   -  ``db_select()`` will now always (if required by the driver) be called by ``db_connect()`` instead of only when initializing.\n   -  Replaced the ``_error_message()`` and ``_error_number()`` methods with ``error()``, which returns an array containing the last database error code and message.\n   -  Improved ``version()`` implementation so that drivers that have a native function to get the version number don't have to be defined in the core ``DB_driver`` class.\n   -  Added capability for packages to hold *config/database.php* config files.\n   -  Added MySQL client compression support.\n   -  Added encrypted connections support (for *mysql*, *sqlsrv* and PDO with *sqlsrv*).\n   -  Removed :doc:`Loader Class <libraries/loader>` from Database error tracing to better find the likely culprit.\n   -  Added support for SQLite3 database driver.\n   -  Added Interbase/Firebird database support via the *ibase* driver.\n   -  Added ODBC support for ``create_database()``, ``drop_database()`` and ``drop_table()`` in :doc:`Database Forge <database/forge>`.\n   -  Added support to binding arrays as ``IN()`` sets in ``query()``.\n\n   -  :doc:`Query Builder <database/query_builder>` changes include:\n\n      - Renamed the Active Record class to Query Builder to remove confusion with the Active Record design pattern.\n      - Added the ability to insert objects with ``insert_batch()``.\n      - Added new methods that return the SQL string of queries without executing them: ``get_compiled_select()``, ``get_compiled_insert()``, ``get_compiled_update()``, ``get_compiled_delete()``.\n      - Added an optional parameter that allows to disable escaping (useful for custom fields) for methods ``join()``, ``order_by()``, ``where_in()``, ``or_where_in()``, ``where_not_in()``, ``or_where_not_in()``, ``insert()``, ``insert_batch()``.\n      - Added support for ``join()`` with multiple conditions.\n      - Added support for *USING* in ``join()``.\n      - Added support for *EXISTS* in ``where()``.\n      - Added seed values support for random ordering with ``order_by(seed, 'RANDOM')``.\n      - Changed ``limit()`` to ignore NULL values instead of always casting to integer.\n      - Changed ``offset()`` to ignore empty values instead of always casting to integer.\n      - Methods ``insert_batch()`` and ``update_batch()`` now return an integer representing the number of rows affected by them.\n      - Methods ``where()``, ``or_where()``, ``having()`` and ``or_having()`` now convert trailing  ``=`` and ``<>``,  ``!=`` SQL operators to ``IS NULL`` and ``IS NOT NULL`` respectively when the supplied comparison value is ``NULL``.\n      - Added method chaining support to ``reset_query()``, ``start_cache()``, ``stop_cache()`` and ``flush_cache()``.\n      - Added an optional second parameter to ``count_all_results()`` to disable resetting of QB values.\n\n   -  :doc:`Database Results <database/results>` changes include:\n\n      - Added a constructor to the ``DB_result`` class and moved all driver-specific properties and logic out of the base ``DB_driver`` class to allow better abstraction.\n      - Added method ``unbuffered_row()`` for fetching a row without prefetching the whole result (consume less memory).\n      - Renamed former method ``_data_seek()`` to ``data_seek()`` and made it public.\n\n   -  Improved support for the MySQLi driver, including:\n\n      - OOP style usage of the PHP extension is now used, instead of the procedural aliases.\n      - Server version checking is now done via ``mysqli::$server_info`` instead of running an SQL query.\n      - Added persistent connections support for PHP >= 5.3.\n      - Added support for configuring socket pipe connections.\n      - Added support for ``backup()`` in :doc:`Database Utilities <database/utilities>`.\n      - Changed methods ``trans_begin()``, ``trans_commit()`` and ``trans_rollback()`` to use the PHP API instead of sending queries.\n\n   -  Improved support of the PDO driver, including:\n\n      - Added support for ``create_database()``, ``drop_database()`` and ``drop_table()`` in :doc:`Database Forge <database/forge>`.\n      - Added support for ``list_fields()`` in :doc:`Database Results <database/results>`.\n      - Subdrivers are now isolated from each other instead of being in one large class.\n\n   -  Improved support of the PostgreSQL driver, including:\n\n      - ``pg_version()`` is now used to get the database version number, when possible.\n      - Added ``db_set_charset()`` support.\n      - Added support for ``optimize_table()`` in :doc:`Database Utilities <database/utilities>` (rebuilds table indexes).\n      - Added boolean data type support in ``escape()``.\n      - Added ``update_batch()`` support.\n      - Removed ``limit()`` and ``order_by()`` support for *UPDATE* and *DELETE* queries as PostgreSQL does not support those features.\n      - Added a work-around for dead persistent connections to be re-created after a database restart.\n      - Changed ``db_connect()`` to include the (new) **schema** value into Postgre's **search_path** session variable.\n      - ``pg_escape_literal()`` is now used for escaping strings, if available.\n\n   -  Improved support of the CUBRID driver, including:\n\n      - Added DSN string support.\n      - Added persistent connections support.\n      - Improved ``list_databases()`` in :doc:`Database Utility <database/utilities>` (until now only the currently used database was returned).\n\n   -  Improved support of the MSSQL and SQLSRV drivers, including:\n\n      - Added random ordering support.\n      - Added support for ``optimize_table()`` in :doc:`Database Utility <database/utilities>`.\n      - Added escaping with *QUOTE_IDENTIFIER* setting detection.\n      - Added port handling support for UNIX-based systems (MSSQL driver).\n      - Added *OFFSET* support for SQL Server 2005 and above.\n      - Added ``db_set_charset()`` support (MSSQL driver).\n      - Added a *scrollable* property to enable configuration of the cursor to use (SQLSRV driver).\n      - Added support and auto-detection for the ``SQLSRV_CURSOR_CLIENT_BUFFERED`` scrollable cursor flag (SQLSRV driver).\n      - Changed default behavior to not use ``SQLSRV_CURSOR_STATIC`` due to performance issues (SQLSRV driver).\n\n   -  Improved support of the Oracle (OCI8) driver, including:\n\n      - Added DSN string support (Easy Connect and TNS).\n      - Added support for ``drop_table()`` in :doc:`Database Forge <database/forge>`.\n      - Added support for ``list_databases()`` in :doc:`Database Utilities <database/utilities>`.\n      - Generally improved for speed and cleaned up all of its components.\n      - ``num_rows()`` is now only called explicitly by the developer and no longer re-executes statements.\n\n   -  Improved support of the SQLite driver, including:\n\n      - Added support for ``replace()`` in :doc:`Query Builder <database/query_builder>`.\n      - Added support for ``drop_table()`` in :doc:`Database Forge <database/forge>`.\n\n   -  :doc:`Database Forge <database/forge>` changes include:\n\n      - Added an optional second parameter to ``drop_table()`` that allows adding the **IF EXISTS** condition, which is no longer the default.\n      - Added support for passing a custom database object to the loader.\n      - Added support for passing custom table attributes (such as ``ENGINE`` for MySQL) to ``create_table()``.\n      - Added support for usage of the *FIRST* clause in ``add_column()`` for MySQL and CUBRID.\n      - Added partial support for field comments (MySQL, PostgreSQL, Oracle).\n      - Deprecated ``add_column()``'s third method. *AFTER* clause should now be added to the field definition array instead.\n      - Overall improved support for all of the drivers.\n\n   -  :doc:`Database Utility <database/utilities>` changes include:\n\n      - Added support for passing a custom database object to the loader.\n      - Modified the class to no longer extend :doc:`Database Forge <database/forge>`, which has been a deprecated behavior for awhile.\n      - Overall improved support for all of the drivers.\n      - Added *foreign_key_checks* option to MySQL/MySQLi backup, allowing statement to disable/re-enable foreign key checks to be inserted into the backup output.\n\n-  Libraries\n\n   -  Added a new :doc:`Encryption Library <libraries/encryption>` to replace the old, largely insecure **Encrypt Library**.\n\n   -  **Encrypt Library** changes include:\n\n      -  Deprecated the library in favor of the new :doc:`Encryption Library <libraries/encryption>`.\n      -  Added support for hashing algorithms other than SHA1 and MD5.\n      -  Removed previously deprecated ``sha1()`` method.\n\n   -  :doc:`Session Library <libraries/sessions>` changes include:\n\n      -  Completely re-written the library to use self-contained drivers via ``$config['sess_driver']``.\n      -  Added 'files', 'database', 'redis' and 'memcached' drivers (using 'files' by default).\n      -  Added ``$config['sess_save_path']`` setting to specify where the session data is stored, depending on the driver.\n      -  Dropped support for storing session data in cookies (which renders ``$config['sess_encrypt_cookie']`` useless and is therefore also removed).\n      -  Dropped official  support for storing session data in databases other than MySQL and PostgreSQL.\n      -  Changed table structure for the 'database' driver.\n      -  Added a new **tempdata** feature that allows setting userdata items with expiration time (``mark_as_temp()``, ``tempdata()``, ``set_tempdata()``, ``unset_tempdata()``).\n      -  Changed method ``keep_flashdata()`` to also accept an array of keys.\n      -  Changed methods ``userdata()``, ``flashdata()`` to return an array of all userdata/flashdata when no parameter is passed.\n      -  Deprecated method ``all_userdata()`` - it is now just an alias for ``userdata()`` with no parameters.\n      -  Added method ``has_userdata()`` that verifies the existence of a userdata item.\n      -  Added *debug* level log messages for key events in the session validation process.\n      -  Dropped support for the *sess_match_useragent* option.\n\n   -  :doc:`File Uploading Library <libraries/file_uploading>` changes include:\n\n      -  Added method chaining support.\n      -  Added support for using array notation in file field names.\n      -  Added **max_filename_increment** and **file_ext_tolower** configuration settings.\n      -  Added **min_width** and **min_height** configuration settings for images.\n      -  Added **mod_mime_fix** configuration setting to disable suffixing multiple file extensions with an underscore.\n      -  Added the possibility pass **allowed_types** as an array.\n      -  Added an ``$index`` parameter to the method ``data()``.\n      -  Added a ``$reset`` parameter to method ``initialize()``.\n      -  Removed method ``clean_file_name()`` and its usage in favor of :doc:`Security Library <libraries/security>`'s ``sanitize_filename()``.\n      -  Removed method ``mimes_types()``.\n      -  Changed ``CI_Upload::_prep_filename()`` to simply replace all (but the last) dots in the filename with underscores, instead of suffixing them.\n\n   -  :doc:`Calendar Library <libraries/calendar>` changes include:\n\n      -  Added method chaining support.\n      -  Added configuration to generate days of other months instead of blank cells.\n      -  Added auto-configuration for *next_prev_url* if it is empty and *show_prev_next* is set to TRUE.\n      -  Added support for templating via an array in addition to the encoded string.\n      -  Changed method ``get_total_days()`` to be an alias for :doc:`Date Helper <helpers/date_helper>` :php:func:`days_in_month()`.\n\n   -  *Cart Library* changes include:\n\n      -  Deprecated the library as too specific for CodeIgniter.\n      -  Added method ``remove()`` to remove a cart item, updating with quantity of 0 seemed like a hack but has remained to retain compatibility.\n      -  Added method ``get_item()`` to enable retrieving data for a single cart item.\n      -  Added unicode support for product names.\n      -  Added support for disabling product name strictness via the ``$product_name_safe`` property.\n      -  Changed ``insert()`` method to auto-increment quantity for an item when inserted twice instead of resetting it.\n      -\t Changed ``update()`` method to support updating all properties attached to an item and not to require 'qty'.\n\n   -  :doc:`Image Manipulation Library <libraries/image_lib>` changes include:\n\n      -  The ``initialize()`` method now only sets existing class properties.\n      -  Added support for 3-length hex color values for *wm_font_color* and *wm_shadow_color* properties, as well as validation for them.\n      -  Class properties *wm_font_color*, *wm_shadow_color* and *wm_use_drop_shadow* are now protected, to avoid breaking the ``text_watermark()`` method if they are set manually after initialization.\n      -  If property *maintain_ratio* is set to TRUE, ``image_reproportion()`` now doesn't need both width and height to be specified.\n      -  Property *maintain_ratio* is now taken into account when resizing images using ImageMagick library.\n      -  Added support for maintaining transparency for PNG images when watermarking.\n      -  Added a **file_permissions** setting.\n\n   -  :doc:`Form Validation Library <libraries/form_validation>` changes include:\n\n      -  Added method ``error_array()`` to return all error messages as an array.\n      -  Added method ``set_data()`` to set an alternative data array to be validated instead of the default ``$_POST``.\n      -  Added method ``reset_validation()`` which resets internal validation variables in case of multiple validation routines.\n      -  Added support for setting error delimiters in the config file via ``$config['error_prefix']`` and ``$config['error_suffix']``.\n      -  Internal method ``_execute()`` now considers input data to be invalid if a specified rule is not found.\n      -  Removed method ``is_numeric()`` as it exists as a native PHP function and ``_execute()`` will find and use that (the **is_numeric** rule itself is deprecated since 1.6.1).\n      -  Native PHP functions used as rules can now accept an additional parameter, other than the data itself.\n      -  Updated method ``set_rules()`` to accept an array of rules as well as a string.\n      -  Fields that have empty rules set no longer run through validation (and therefore are not considered erroneous).\n      -  Added rule **differs** to check if the value of a field differs from the value of another field.\n      -  Added rule **valid_url**.\n      -  Added rule **in_list** to check if the value of a field is within a given list.\n      -  Added support for named parameters in error messages.\n      -  :doc:`Language <libraries/language>` line keys must now be prefixed with **form_validation_**.\n      -  Added rule **alpha_numeric_spaces**.\n      -  Added support for custom error messages per field rule.\n      -  Added support for callable rules when they are passed as an array.\n      -  Added support for non-ASCII domains in **valid_email** rule, depending on the Intl extension.\n      -  Changed the debug message about an error message not being set to include the rule name it is about.\n\n   -  :doc:`Caching Library <libraries/caching>` changes include:\n\n      -  Added Wincache driver.\n      -  Added Redis driver.\n      -  Added a *key_prefix* option for cache IDs.\n      -  Updated driver ``is_supported()`` methods to log at the \"debug\" level.\n      -  Added option to store raw values instead of CI-formatted ones (APC, Memcache).\n      -  Added atomic increment/decrement feature via ``increment()``, ``decrement()``.\n\n   -  :doc:`E-mail Library <libraries/email>` changes include:\n\n      -  Added a custom filename parameter to ``attach()`` as ``$this->email->attach($filename, $disposition, $newname)``.\n      -  Added possibility to send attachment as buffer string in ``attach()`` as ``$this->email->attach($buffer, $disposition, $newname, $mime)``.\n      -  Added possibility to attach remote files by passing a URL.\n      -  Added method ``attachment_cid()`` to enable embedding inline attachments into HTML.\n      -  Added dsn (delivery status notification) option.\n      -  Renamed method ``_set_header()`` to ``set_header()`` and made it public to enable adding custom headers.\n      -  Successfully sent emails will automatically clear the parameters.\n      -  Added a *return_path* parameter to the ``from()`` method.\n      -  Removed the second parameter (character limit) from internal method ``_prep_quoted_printable()`` as it is never used.\n      -  Internal method ``_prep_quoted_printable()`` will now utilize the native ``quoted_printable_encode()``, ``imap_8bit()`` functions (if available) when CRLF is set to \"\\r\\n\".\n      -  Default charset now relies on the global ``$config['charset']`` setting.\n      -  Removed unused protected method ``_get_ip()`` (:doc:`Input Library <libraries/input>`'s ``ip_address()`` should be used anyway).\n      -  Internal method ``_prep_q_encoding()`` now utilizes PHP's *mbstring* and *iconv* extensions (when available) and no longer has a second (``$from``) argument.\n      -  Added an optional parameter to ``print_debugger()`` to allow specifying which parts of the message should be printed ('headers', 'subject', 'body').\n      -  Added SMTP keepalive option to avoid opening the connection for each ``send()`` call. Accessible as ``$smtp_keepalive``.\n      -  Public method ``set_header()`` now filters the input by removing all \"\\\\r\" and \"\\\\n\" characters.\n      -  Added support for non-ASCII domains in ``valid_email()``, depending on the Intl extension.\n\n   -  :doc:`Pagination Library <libraries/pagination>` changes include:\n\n      -  Deprecated usage of the \"anchor_class\" setting (use the new \"attributes\" setting instead).\n      -  Added method chaining support to ``initialize()`` method.\n      -  Added support for the anchor \"rel\" attribute.\n      -  Added support for setting custom attributes.\n      -  Added support for language translations of the *first_link*, *next_link*, *prev_link* and *last_link* values.\n      -  Added support for ``$config['num_links'] = 0`` configuration.\n      -  Added ``$config['reuse_query_string']`` to allow automatic repopulation of query string arguments, combined with normal URI segments.\n      -  Added ``$config['use_global_url_suffix']`` to allow overriding the library 'suffix' value with that of the global ``$config['url_suffix']`` setting.\n      -  Removed the default ``&nbsp;`` from a number of the configuration variables.\n\n   -  :doc:`Profiler Library <general/profiling>` changes include:\n\n      -  Database object names are now being displayed.\n      -  The sum of all queries running times in seconds is now being displayed.\n      -  Added support for displaying the HTTP DNT (\"Do Not Track\") header.\n      -  Added support for displaying ``$_FILES``.\n\n   -  :doc:`Migration Library <libraries/migration>` changes include:\n\n      -  Added support for timestamp-based migrations (enabled by default).\n      -  Added ``$config['migration_type']`` to allow switching between *sequential* and *timestamp* migrations.\n\n   -  :doc:`XML-RPC Library <libraries/xmlrpc>` changes include:\n\n      -  Added the ability to use a proxy.\n      -  Added Basic HTTP authentication support.\n\n   -  :doc:`User Agent Library <libraries/user_agent>` changes include:\n\n      - Added check to detect if robots are pretending to be mobile clients (helps with e.g. Google indexing mobile website versions).\n      - Added method ``parse()`` to allow parsing a custom user-agent string, different from the current visitor's.\n\n   -  :doc:`HTML Table Library <libraries/table>` changes include:\n\n      - Added method chaining support.\n      - Added support for setting table class defaults in a config file.\n\n   -  :doc:`Zip Library <libraries/zip>` changes include:\n\n      - Method ``read_file()`` can now also alter the original file path/name while adding files to an archive.\n      - Added support for changing the compression level.\n\n   -  :doc:`Trackback Library <libraries/trackback>` method ``receive()`` will now utilize ``iconv()`` if it is available but ``mb_convert_encoding()`` is not.\n\n-  Core\n\n   -  :doc:`Routing <general/routing>` changes include:\n\n      -  Added support for multiple levels of controller directories.\n      -  Added support for per-directory *default_controller* and *404_override* classes.\n      -  Added possibility to route requests using HTTP verbs.\n      -  Added possibility to route requests using callbacks.\n      -  Added a new reserved route (*translate_uri_dashes*) to allow usage of dashes in the controller and method URI segments.\n      -  Deprecated methods ``fetch_directory()``, ``fetch_class()`` and ``fetch_method()`` in favor of their respective public properties.\n      -  Removed method ``_set_overrides()`` and moved its logic to the class constructor.\n\n   -  :doc:`URI Library <libraries/uri>` changes include:\n\n      -  Added conditional PCRE UTF-8 support to the \"invalid URI characters\" check and removed the ``preg_quote()`` call from it to allow more flexibility.\n      -  Renamed method ``_filter_uri()`` to ``filter_uri()``.\n      -  Changed method ``filter_uri()`` to accept by reference and removed its return value.\n      -  Changed private methods to protected so that MY_URI can override them.\n      -  Renamed internal method ``_parse_cli_args()`` to ``_parse_argv()``.\n      -  Renamed internal method ``_detect_uri()`` to ``_parse_request_uri()``.\n      -  Changed ``_parse_request_uri()`` to accept absolute URIs for compatibility with HTTP/1.1 as per `RFC2616 <https://www.ietf.org/rfc/rfc2616.txt>`.\n      -  Added protected method ``_parse_query_string()`` to URI paths in the the **QUERY_STRING** value, like ``_parse_request_uri()`` does.\n      -  Changed URI string detection logic to always default to **REQUEST_URI** unless configured otherwise or under CLI.\n      -  Removed methods ``_remove_url_suffix()``, ``_explode_segments()`` and moved their logic into ``_set_uri_string()``.\n      -  Removed method ``_fetch_uri_string()`` and moved its logic into the class constructor.\n      -  Removed method ``_reindex_segments()``.\n\n   -  :doc:`Loader Library <libraries/loader>` changes include:\n\n      -  Added method chaining support.\n      -  Added method ``get_vars()`` to the Loader to retrieve all variables loaded with ``$this->load->vars()``.\n      -  ``_ci_autoloader()`` is now a protected method.\n      -  Added autoloading of drivers with ``$autoload['drivers']``.\n      -  ``$config['rewrite_short_tags']`` now has no effect when using PHP 5.4 as ``<?=`` will always be available.\n      -  Changed method ``config()`` to return whatever ``CI_Config::load()`` returns instead of always being void.\n      -  Added support for library and model aliasing on autoload.\n      -  Changed method ``is_loaded()`` to ask for the (case sensitive) library name instead of its instance name.\n      -  Removed ``$_base_classes`` property and unified all class data in ``$_ci_classes`` instead.\n      -  Added method ``clear_vars()`` to allow clearing the cached variables for views.\n\n   -  :doc:`Input Library <libraries/input>` changes include:\n\n      -  Deprecated the ``$config['global_xss_filtering']`` setting.\n      -  Added ``method()`` to retrieve ``$_SERVER['REQUEST_METHOD']``.\n      -  Added support for arrays and network addresses (e.g. 192.168.1.1/24) for use with the *proxy_ips* setting.\n      -  Added method ``input_stream()`` to aid in using **php://input** stream data such as one passed via PUT, DELETE and PATCH requests.\n      -  Changed method ``valid_ip()`` to use PHP's native ``filter_var()`` function.\n      -  Changed internal method ``_sanitize_globals()`` to skip enforcing reversal of *register_globals* in PHP 5.4+, where this functionality no longer exists.\n      -  Changed methods ``get()``, ``post()``, ``get_post()``, ``cookie()``, ``server()``, ``user_agent()`` to return NULL instead of FALSE when no value is found.\n      -  Changed default value of the ``$xss_clean`` parameter to NULL for all methods that utilize it, the default value is now determined by the ``$config['global_xss_filtering']`` setting.\n      -  Added method ``post_get()`` and changed ``get_post()`` to search in GET data first. Both methods' names now properly match their GET/POST data search priorities.\n      -  Changed method ``_fetch_from_array()`` to parse array notation in field name.\n      -  Changed method ``_fetch_from_array()`` to allow retrieving multiple fields at once.\n      -  Added an option for ``_clean_input_keys()`` to return FALSE instead of terminating the whole script.\n      -  Deprecated the ``is_cli_request()`` method, it is now an alias for the new :php:func:`is_cli()` common function.\n      -  Added an ``$xss_clean`` parameter to method ``user_agent()`` and removed the ``$user_agent`` property.\n      -  Added property ``$raw_input_stream`` to access **php://input** data.\n\n   -  :doc:`Common functions <general/common_functions>` changes include:\n\n      -  Added function :php:func:`get_mimes()` to return the *application/config/mimes.php* array.\n      -  Added support for HTTP code 303 (\"See Other\") in :php:func:`set_status_header()`.\n      -  Removed redundant conditional to determine HTTP server protocol in :php:func:`set_status_header()`.\n      -  Renamed ``_exception_handler()`` to ``_error_handler()`` and replaced it with a real exception handler.\n      -  Changed ``_error_handler()`` to respect php.ini *display_errors* setting.\n      -  Added function :php:func:`is_https()` to check if a secure connection is used.\n      -  Added function :php:func:`is_cli()` to replace the ``CI_Input::is_cli_request()`` method.\n      -  Added function :php:func:`function_usable()` to work around a bug in `Suhosin <http://www.hardened-php.net/suhosin/>`.\n      -  Removed the third (`$php_error`) argument from function :php:func:`log_message()`.\n      -  Changed internal function ``load_class()`` to accept a constructor parameter instead of (previously unused) class name prefix.\n      -  Removed default parameter value of :php:func:`is_php()`.\n      -  Added a second argument ``$double_encode`` to :php:func:`html_escape()`.\n      -  Changed function :php:func:`config_item()` to return NULL instead of FALSE when no value is found.\n      -  Changed function :php:func:`set_status_header()` to return immediately when run under CLI.\n\n   -  :doc:`Output Library <libraries/output>` changes include:\n\n      -  Added a second argument to method ``set_content_type()`` that allows setting the document charset as well.\n      -  Added methods ``get_content_type()`` and ``get_header()``.\n      -  Added method ``delete_cache()``.\n      -  Added configuration option ``$config['cache_query_string']`` to enable taking the query string into account when caching.\n      -  Changed caching behavior to compress the output before storing it, if ``$config['compress_output']`` is enabled.\n\n   -  :doc:`Config Library <libraries/config>` changes include:\n\n      -  Changed ``site_url()`` method  to accept an array as well.\n      -  Removed internal method ``_assign_to_config()`` and moved its implementation to *CodeIgniter.php* instead.\n      -  ``item()`` now returns NULL instead of FALSE when the required config item doesn't exist.\n      -  Added an optional second parameter to both ``base_url()`` and ``site_url()`` that allows enforcing of a protocol different than the one in the *base_url* configuration setting.\n      -  Added HTTP \"Host\" header character validation to prevent cache poisoning attacks when ``base_url`` auto-detection is used.\n\n   -  :doc:`Security Library <libraries/security>` changes include:\n\n      -  Added ``$config['csrf_regeneration']``, which makes CSRF token regeneration optional.\n      -  Added ``$config['csrf_exclude_uris']``, allowing for exclusion of URIs from the CSRF protection (regular expressions are supported).\n      -  Added method ``strip_image_tags()``.\n      -  Added method ``get_random_bytes()`` and switched CSRF & XSS token generation to use it.\n      -  Modified method ``sanitize_filename()`` to read a public ``$filename_bad_chars`` property for getting the invalid characters list.\n      -  Return status code of 403 instead of a 500 if CSRF protection is enabled but a token is missing from a request.\n\n   -  :doc:`Language Library <libraries/language>` changes include:\n\n      -  Changed method ``load()`` to filter the language name with ``ctype_alpha()``.\n      -  Changed method ``load()`` to also accept an array of language files.\n      -  Added an optional second parameter to method ``line()`` to disable error logging for line keys that were not found.\n      -  Language files are now loaded in a cascading style with the one in **system/** always loaded and overridden afterwards, if another one is found.\n\n   -  :doc:`Hooks Library <general/hooks>` changes include:\n\n      -  Added support for closure hooks (or anything that ``is_callable()`` returns TRUE for).\n      -  Renamed method ``_call_hook()`` to ``call_hook()``.\n      -  Class instances are now stored in order to maintain their state.\n\n   -  UTF-8 Library changes include:\n\n      -  ``UTF8_ENABLED`` now requires only one of `Multibyte String <https://secure.php.net/mbstring>`_ or `iconv <https://secure.php.net/iconv>`_ to be available instead of both.\n      -  Changed method ``clean_string()`` to utilize ``mb_convert_encoding()`` if it is available.\n      -  Renamed method ``_is_ascii()`` to ``is_ascii()`` and made it public.\n\n   -  Log Library changes include:\n\n      -  Added a ``$config['log_file_permissions']`` setting.\n      -  Changed the library constructor to try to create the **log_path** directory if it doesn't exist.\n      -  Added support for microseconds (\"u\" date format character) in ``$config['log_date_format']``.\n\n   -  Added :doc:`compatibility layers <general/compatibility_functions>` for:\n\n      - `Multibyte String <https://secure.php.net/mbstring>`_ (limited support).\n      - `Hash <https://secure.php.net/hash>`_ (``hash_equals()``, ``hash_pbkdf2()``).\n      - `Password Hashing <https://secure.php.net/password>`_.\n      - `Standard Functions ``array_column()``, ``array_replace()``, ``array_replace_recursive()``, ``hex2bin()``, ``quoted_printable_encode()``.\n\n   -  Removed ``CI_CORE`` boolean constant from *CodeIgniter.php* (no longer Reactor and Core versions).\n   -  Added support for HTTP-Only cookies with new config option *cookie_httponly* (default FALSE).\n   -  ``$config['time_reference']`` now supports all timezone strings supported by PHP.\n   -  Fatal PHP errors are now also passed to ``_error_handler()``, so they can be logged.\n\n\nBug fixes for 3.0\n-----------------\n\n-  Fixed a bug where ``unlink()`` raised an error if cache file did not exist when you try to delete it.\n-  Fixed a bug (#181) - a typo in the form validation language file.\n-  Fixed a bug (#159, #163) - :doc:`Query Builder <database/query_builder>` nested transactions didn't work properly due to ``$_trans_depth`` not being incremented.\n-  Fixed a bug (#737, #75) - :doc:`Pagination <libraries/pagination>` anchor class was not set properly when using initialize method.\n-  Fixed a bug (#419) - :doc:`URL Helper <helpers/url_helper>` :php:func:`auto_link()` didn't recognize URLs that come after a word boundary.\n-  Fixed a bug (#724) - :doc:`Form Validation Library <libraries/form_validation>` rule **is_unique** didn't check if a database connection exists.\n-  Fixed a bug (#647) - :doc:`Zip Library <libraries/zip>` internal method ``_get_mod_time()`` didn't suppress possible \"stat failed\" errors generated by ``filemtime()``.\n-  Fixed a bug (#157, #174) - :doc:`Image Manipulation Library <libraries/image_lib>` method ``clear()`` didn't completely clear properties.\n-  Fixed a bug where :doc:`Database Forge <database/forge>` method ``create_table()`` with PostgreSQL database could lead to fetching the whole table.\n-  Fixed a bug (#795) - :doc:`Form Helper <helpers/form_helper>` :php:func:`form_open()` didn't add the default form *method* and *accept-charset* when an empty array is passed to it.\n-  Fixed a bug (#797) - :doc:`Date Helper <helpers/date_helper>` :php:func:`timespan()` was using incorrect seconds for year and month.\n-  Fixed a bug in *Cart Library* method ``contents()`` where if called without a TRUE (or equal) parameter, it would fail due to a typo.\n-  Fixed a bug (#406) - SQLSRV DB driver not returning resource on ``db_pconnect()``.\n-  Fixed a bug in :doc:`Image Manipulation Library <libraries/image_lib>` method ``gd_loaded()`` where it was possible for the script execution to end or a PHP E_WARNING message to be emitted.\n-  Fixed a bug in the :doc:`Pagination library <libraries/pagination>` where when use_page_numbers=TRUE previous link and page 1 link did not have the same url.\n-  Fixed a bug (#561) - errors in :doc:`XML-RPC Library <libraries/xmlrpc>` were not properly escaped.\n-  Fixed a bug (#904) - :doc:`Loader Library <libraries/loader>` method ``initialize()`` caused a PHP Fatal error to be triggered if error level E_STRICT is used.\n-  Fixed a hosting edge case where an empty ``$_SERVER['HTTPS']`` variable would evaluate to 'on'.\n-  Fixed a bug (#154) - :doc:`Session Library <libraries/sessions>` method ``sess_update()`` caused the session to be destroyed on pages where multiple AJAX requests were executed at once.\n-  Fixed a possible bug in :doc:`Input Libary <libraries/input>` method ``is_ajax_request()`` where some clients might not send the X-Requested-With HTTP header value exactly as 'XmlHttpRequest'.\n-  Fixed a bug (#1039) - :doc:`Database Utilities <database/utilities>` internal method ``_backup()`` method failed for the 'mysql' driver due to a table name not being escaped.\n-  Fixed a bug (#1070) - ``CI_DB_driver::initialize()`` didn't set a character set if a database is not selected.\n-  Fixed a bug (#177) - :doc:`Form Validation Library <libraries/form_validation>` method ``set_value()`` didn't set the default value if POST data is NULL.\n-  Fixed a bug (#68, #414) - :Oracle's ``escape_str()`` didn't properly escape LIKE wild characters.\n-  Fixed a bug (#81) - ODBC's ``list_fields()`` and ``field_data()`` methods skipped the first column due to ``odbc_field_*()`` functions' index starting at 1 instead of 0.\n-  Fixed a bug (#129) - ODBC's ``num_rows()`` method returned -1 in some cases, due to not all subdrivers supporting the ``odbc_num_rows()`` function.\n-  Fixed a bug (#153) - E_NOTICE being generated by ``getimagesize()`` in the :doc:`File Uploading Library <libraries/file_uploading>`.\n-  Fixed a bug (#611) - SQLSRV's error handling methods used to issue warnings when there's no actual error.\n-  Fixed a bug (#1036) - ``is_write_type()`` method in the :doc:`Database Library <database/index>` didn't return TRUE for RENAME queries.\n-  Fixed a bug in PDO's ``_version()`` method where it used to return the client version as opposed to the server one.\n-  Fixed a bug in PDO's ``insert_id()`` method where it could've failed if it's used with Postgre versions prior to 8.1.\n-  Fixed a bug in CUBRID's ``affected_rows()`` method where a connection resource was passed to ``cubrid_affected_rows()`` instead of a result.\n-  Fixed a bug (#638) - ``db_set_charset()`` ignored its arguments and always used the configured charset instead.\n-  Fixed a bug (#413) - Oracle's error handling methods used to only return connection-related errors.\n-  Fixed a bug (#1101) - :doc:`Database Result <database/results>` method ``field_data()`` for 'mysql', 'mysqli' drivers was implemented as if it was handling a DESCRIBE result instead of the actual result set.\n-  Fixed a bug in Oracle's :doc:`Database Forge <database/forge>` method ``_create_table()`` where it failed with AUTO_INCREMENT as it's not supported.\n-  Fixed a bug (#1080) - when using the SMTP protocol, :doc:`Email Library <libraries/email>` method ``send()`` was returning TRUE even if the connection/authentication against the server failed.\n-  Fixed a bug (#306) - ODBC's ``insert_id()`` method was calling non-existent function ``odbc_insert_id()``, which resulted in a fatal error.\n-  Fixed a bug in Oracle's :doc:`Database Result <database/results>` implementation where the cursor ID passed to it was always NULL.\n-  Fixed a bug (#64) - Regular expression in *DB_query_builder.php* failed to handle queries containing SQL bracket delimiters in the JOIN condition.\n-  Fixed a bug in the :doc:`Session Library <libraries/sessions>` where a PHP E_NOTICE error was triggered by ``_unserialize()`` due to results from databases such as MSSQL and Oracle being space-padded on the right.\n-  Fixed a bug (#501) - :doc:`Form Validation Library <libraries/form_validation>` method ``set_rules()`` depended on ``count($_POST)`` instead of actually checking if the request method 'POST' before aborting.\n-  Fixed a bug (#136) - PostgreSQL and MySQL's ``escape_str()`` method didn't properly escape LIKE wild characters.\n-  Fixed a bug in :doc:`Loader Library <libraries/loader>` method ``library()`` where some PHP versions wouldn't execute the class constructor.\n-  Fixed a bug (#88) - An unexisting property was used for configuration of the Memcache cache driver.\n-  Fixed a bug (#14) - :doc:`Database Forge <database/forge>` method ``create_database()`` didn't utilize the configured database character set.\n-  Fixed a bug (#23, #1238) - :doc:`Database Caching <database/caching>` method ``delete_all()`` used to delete .htaccess and index.html files, which is a potential security risk.\n-  Fixed a bug in :doc:`Trackback Library <libraries/trackback>` method ``validate_url()`` where it didn't actually do anything, due to input not being passed by reference.\n-  Fixed a bug (#11, #183, #863) - :doc:`Form Validation Library <libraries/form_validation>` method ``_execute()`` silently continued to the next rule, if a rule method/function is not found.\n-  Fixed a bug (#122) - routed URI string was being reported incorrectly in sub-directories.\n-  Fixed a bug (#1241) - :doc:`Zip Library <libraries/zip>` method ``read_dir()`` wasn't compatible with Windows.\n-  Fixed a bug (#306) - ODBC driver didn't have an ``_insert_batch()`` method, which resulted in fatal error being triggered when ``insert_batch()`` is used with it.\n-  Fixed a bug in MSSQL and SQLSrv's ``_truncate()`` where the TABLE keyword was missing.\n-  Fixed a bug in PDO's ``trans_commit()`` method where it failed due to an erroneous property name.\n-  Fixed a bug (#798) - :doc:`Query Builder <database/query_builder>` method ``update()`` used to ignore LIKE conditions that were set with ``like()``.\n-  Fixed a bug in Oracle's and MSSQL's ``delete()`` methods where an erroneous SQL statement was generated when used with ``limit()``.\n-  Fixed a bug in SQLSRV's ``delete()`` method where ``like()`` and ``limit()`` conditions were ignored.\n-  Fixed a bug (#1265) - Database connections were always closed, regardless of the 'pconnect' option value.\n-  Fixed a bug (#128) - :doc:`Language Library <libraries/language>` did not correctly keep track of loaded language files.\n-  Fixed a bug (#1349) - :doc:`File Uploading Library <libraries/file_uploading>` method ``get_extension()`` returned the original filename when it didn't have an actual extension.\n-  Fixed a bug (#1273) - :doc:`Query Builder <database/query_builder>` method ``set_update_batch()`` generated an E_NOTICE message.\n-  Fixed a bug (#44, #110) - :doc:`File Uploading Library <libraries/file_uploading>` method ``clean_file_name()`` didn't clear '!' and '#' characters.\n-  Fixed a bug (#121) - :doc:`Database Results <database/results>` method ``row()`` returned an array when there's no actual result to be returned.\n-  Fixed a bug (#319) - SQLSRV's ``affected_rows()`` method failed due to a scrollable cursor being created for write-type queries.\n-  Fixed a bug (#356) - :doc:`Database <database/index>` driver 'postgre' didn't have an ``_update_batch()`` method, which resulted in fatal error being triggered when ``update_batch()`` is used with it.\n-  Fixed a bug (#784, #862) - :doc:`Database Forge <database/forge>` method ``create_table()`` failed on SQLSRV/MSSQL when used with 'IF NOT EXISTS'.\n-  Fixed a bug (#1419) - :doc:`Driver Library <general/creating_drivers>` had a static variable that was causing an error.\n-  Fixed a bug (#1411) - the :doc:`Email Library <libraries/email>` used its own short list of MIMEs instead the one from *config/mimes.php*.\n-  Fixed a bug where php.ini setting *magic_quotes_runtime* wasn't turned off for PHP 5.3 (where it is indeed deprecated, but not non-existent).\n-  Fixed a bug (#666) - :doc:`Output Library <libraries/output>` method ``set_content_type()`` didn't set the document charset.\n-  Fixed a bug (#784, #861) - :doc:`Database Forge <database/forge>` method ``create_table()`` used to accept constraints for MSSQL/SQLSRV integer-type columns.\n-  Fixed a bug (#706) - SQLSRV/MSSSQL :doc:`Database <database/index>` drivers didn't escape field names.\n-  Fixed a bug (#1452) - :doc:`Query Builder <database/query_builder>` method ``protect_identifiers()`` didn't properly detect identifiers with spaces in their names.\n-  Fixed a bug where :doc:`Query Builder <database/query_builder>` method ``protect_identifiers()`` ignored its extra arguments when the value passed to it is an array.\n-  Fixed a bug where :doc:`Query Builder <database/query_builder>` internal method ``_has_operator()`` didn't detect BETWEEN.\n-  Fixed a bug where :doc:`Query Builder <database/query_builder>` method ``join()`` failed with identifiers containing dashes.\n-  Fixed a bug (#1264) - :doc:`Database Forge <database/forge>` and :doc:`Database Utilities <database/utilities>` didn't update/reset the databases and tables list cache when a table or a database is created, dropped or renamed.\n-  Fixed a bug (#7) - :doc:`Query Builder <database/query_builder>` method ``join()`` only escaped one set of conditions.\n-  Fixed a bug (#1321) - ``CI_Exceptions`` couldn't find the *errors/* directory in some cases.\n-  Fixed a bug (#1202) - **Encrypt Library** ``encode_from_legacy()`` didn't set back the encrypt mode on failure.\n-  Fixed a bug (#145) - :doc:`Database Class <database/index>` method ``compile_binds()`` failed when the bind marker was present in a literal string within the query.\n-  Fixed a bug in :doc:`Query Builder <database/query_builder>` method ``protect_identifiers()`` where if passed along with the field names, operators got escaped as well.\n-  Fixed a bug (#10) - :doc:`URI Library <libraries/uri>` internal method ``_detect_uri()`` failed with paths containing a colon.\n-  Fixed a bug (#1387) - :doc:`Query Builder <database/query_builder>` method ``from()`` didn't escape table aliases.\n-  Fixed a bug (#520) - :doc:`Date Helper <helpers/date_helper>` function ``nice_date()`` failed when the optional second parameter is not passed.\n-  Fixed a bug (#318) - :doc:`Profiling Library <general/profiling>` setting *query_toggle_count* was not settable as described in the manual.\n-  Fixed a bug (#938) - :doc:`Config Library <libraries/config>` method ``site_url()`` added a question mark to the URL string when query strings are enabled even if it already existed.\n-  Fixed a bug (#999) - :doc:`Config Library <libraries/config>` method ``site_url()`` always appended ``$config['url_suffix']`` to the end of the URL string, regardless of whether a query string exists in it.\n-  Fixed a bug where :doc:`URL Helper <helpers/url_helper>` function :php:func:`anchor_popup()` ignored the attributes argument if it is not an array.\n-  Fixed a bug (#1328) - :doc:`Form Validation Library <libraries/form_validation>` didn't properly check the type of the form fields before processing them.\n-  Fixed a bug (#79) - :doc:`Form Validation Library <libraries/form_validation>` didn't properly validate array fields that use associative keys or have custom indexes.\n-  Fixed a bug (#427) - :doc:`Form Validation Library <libraries/form_validation>` method ``strip_image_tags()`` was an alias to a non-existent method.\n-  Fixed a bug (#1545) - :doc:`Query Builder <database/query_builder>` method ``limit()`` wasn't executed properly under Oracle.\n-  Fixed a bug (#1551) - :doc:`Date Helper <helpers/date_helper>` function ``standard_date()`` didn't properly format *W3C* and *ATOM* standard dates.\n-  Fixed a bug where :doc:`Query Builder <database/query_builder>` method ``join()`` escaped literal values as if they were fields.\n-  Fixed a bug (#135) - PHP Error logging was impossible without the errors being displayed.\n-  Fixed a bug (#1613) - :doc:`Form Helper <helpers/form_helper>` functions :php:func:`form_multiselect()`, :php:func:`form_dropdown()` didn't properly handle empty array option groups.\n-  Fixed a bug (#1605) - :doc:`Pagination Library <libraries/pagination>` produced incorrect *previous* and *next* link values.\n-  Fixed a bug in SQLSRV's ``affected_rows()`` method where an erroneous function name was used.\n-  Fixed a bug (#1000) - Change syntax of ``$view_file`` to ``$_ci_view_file`` to prevent being overwritten by application.\n-  Fixed a bug (#1757) - :doc:`Directory Helper <helpers/directory_helper>` function :php:func:`directory_map()` was skipping files and directories named '0'.\n-  Fixed a bug (#1789) - :doc:`Database Library <database/index>` method ``escape_str()`` escaped quote characters in LIKE conditions twice under MySQL.\n-  Fixed a bug (#395) - :doc:`Unit Testing Library <libraries/unit_testing>` method ``result()`` didn't properly check array result columns when called from ``report()``.\n-  Fixed a bug (#1692) - :doc:`Database Class <database/index>` method ``display_error()`` didn't properly trace the possible error source on Windows systems.\n-  Fixed a bug (#1745) - :doc:`Database Class <database/index>` method ``is_write_type()`` didn't return TRUE for LOAD queries.\n-  Fixed a bug (#1765) - :doc:`Database Class <database/index>` didn't properly detect connection errors for the 'mysqli' driver.\n-  Fixed a bug (#1257) - :doc:`Query Builder <database/query_builder>` used to (unnecessarily) group FROM clause contents, which breaks certain queries and is invalid for some databases.\n-  Fixed a bug (#1709) - :doc:`Email <libraries/email>` headers were broken when using long email subjects and \\r\\n as CRLF.\n-  Fixed a bug where ``MB_ENABLED`` constant was only declared if ``UTF8_ENABLED`` was set to TRUE.\n-  Fixed a bug where the :doc:`Session Library <libraries/sessions>` accepted cookies with *last_activity* values being in the future.\n-  Fixed a bug (#1897) - :doc:`Email Library <libraries/email>` triggered PHP E_WARNING errors when *mail* protocol used and ``to()`` is never called.\n-  Fixed a bug (#1409) - :doc:`Email Library <libraries/email>` didn't properly handle multibyte characters when applying Q-encoding to headers.\n-  Fixed a bug where :doc:`Email Library <libraries/email>` ignored its *wordwrap* setting while handling alternative messages.\n-  Fixed a bug (#1476, #1909) - :doc:`Pagination Library <libraries/pagination>` didn't take into account actual routing when determining the current page.\n-  Fixed a bug (#1766) - :doc:`Query Builder <database/query_builder>` didn't always take into account the *dbprefix* setting.\n-  Fixed a bug (#779) - :doc:`URI Class <libraries/uri>` didn't always trim slashes from the *uri_string* as shown in the documentation.\n-  Fixed a bug (#134) - :doc:`Database Caching <database/caching>` method ``delete_cache()`` didn't work in some cases due to *cachedir* not being initialized properly.\n-  Fixed a bug (#191) - :doc:`Loader Library <libraries/loader>` ignored attempts for (re)loading databases to ``get_instance()->db`` even when the old database connection is dead.\n-  Fixed a bug (#1255) - :doc:`User Agent Library <libraries/user_agent>` method ``is_referral()`` only checked if ``$_SERVER['HTTP_REFERER']`` exists.\n-  Fixed a bug (#1146) - :doc:`Download Helper <helpers/download_helper>` function :php:func:`force_download()` incorrectly sent *Cache-Control* directives *pre-check* and *post-check* to Internet Explorer.\n-  Fixed a bug (#1811) - :doc:`URI Library <libraries/uri>` didn't properly cache segments for ``uri_to_assoc()`` and ``ruri_to_assoc()``.\n-  Fixed a bug (#1506) - :doc:`Form Helpers <helpers/form_helper>` set empty *name* attributes.\n-  Fixed a bug (#59) - :doc:`Query Builder <database/query_builder>` method ``count_all_results()`` ignored the DISTINCT clause.\n-  Fixed a bug (#1624) - :doc:`Form Validation Library <libraries/form_validation>` rule **matches** didn't property handle array field names.\n-  Fixed a bug (#1630) - :doc:`Form Helper <helpers/form_helper>` function :php:func:`set_value()` didn't escape HTML entities.\n-  Fixed a bug (#142) - :doc:`Form Helper <helpers/form_helper>` function :php:func:`form_dropdown()` didn't escape HTML entities in option values.\n-  Fixed a bug (#50) - :doc:`Session Library <libraries/sessions>` unnecessarily stripped slashed from serialized data, making it impossible to read objects in a namespace.\n-  Fixed a bug (#658) - :doc:`Routing <general/routing>` wildcard **:any** didn't work as advertised and matched multiple URI segments instead of all characters within a single segment.\n-  Fixed a bug (#1938) - :doc:`Email Library <libraries/email>` removed multiple spaces inside a pre-formatted plain text message.\n-  Fixed a bug (#122) - :doc:`URI Library <libraries/uri>` method ``ruri_string()`` didn't include a directory if one is used.\n-  Fixed a bug - :doc:`Routing Library <general/routing>` didn't properly handle *default_controller* in a subdirectory when a method is also specified.\n-  Fixed a bug (#953) - :doc:`post_controller_constructor hook <general/hooks>` wasn't called with a *404_override*.\n-  Fixed a bug (#1220) - :doc:`Profiler Library <general/profiling>` didn't display information for database objects that are instantiated inside models.\n-  Fixed a bug (#1978) - :doc:`Directory Helper <helpers/directory_helper>` function :php:func:`directory_map()`'s return array didn't make a distinction between directories and file indexes when a directory with a numeric name is present.\n-  Fixed a bug (#777) - :doc:`Loader Library <libraries/loader>` didn't look for helper extensions in added package paths.\n-  Fixed a bug (#18) - :doc:`APC Cache <libraries/caching>` driver didn't (un)serialize data, resulting in failure to store objects.\n-  Fixed a bug (#188) - :doc:`Unit Testing Library <libraries/unit_testing>` filled up logs with error messages for non-existing language keys.\n-  Fixed a bug (#113) - :doc:`Form Validation Library <libraries/form_validation>` didn't properly handle empty fields that were specified as an array.\n-  Fixed a bug (#2061) - :doc:`Routing Class <general/routing>` didn't properly sanitize directory, controller and function triggers with **enable_query_strings** set to TRUE.\n-  Fixed a bug - SQLSRV didn't support ``escape_like_str()`` or escaping an array of values.\n-  Fixed a bug - :doc:`Database Results <database/results>` method ``list_fields()`` didn't reset its field pointer for the 'mysql', 'mysqli' and 'mssql' drivers.\n-  Fixed a bug (#2211) - :doc:`Migration Library <libraries/migration>` extensions couldn't execute ``CI_Migration::__construct()``.\n-  Fixed a bug (#2255) - :doc:`Email Library <libraries/email>` didn't apply *smtp_timeout* to socket reads and writes.\n-  Fixed a bug (#2239) - :doc:`Email Library <libraries/email>` improperly handled the Subject when used with *bcc_batch_mode* resulting in E_WARNING messages and an empty Subject.\n-  Fixed a bug (#2234) - :doc:`Query Builder <database/query_builder>` didn't reset JOIN cache for write-type queries.\n-  Fixed a bug (#2298) - :doc:`Database Results <database/results>` method ``next_row()`` kept returning the last row, allowing for infinite loops.\n-  Fixed a bug (#2236, #2639) - :doc:`Form Helper <helpers/form_helper>` functions :php:func:`set_value()`, :php:func:`set_select()`, :php:func:`set_radio()`, :php:func:`set_checkbox()` didn't parse array notation for keys if the rule was not present in the :doc:`Form Validation Library <libraries/form_validation>`.\n-  Fixed a bug (#2353) - :doc:`Query Builder <database/query_builder>` erroneously prefixed literal strings with **dbprefix**.\n-  Fixed a bug (#78) - *Cart Library* didn't allow non-English letters in product names.\n-  Fixed a bug (#77) - :doc:`Database Class <database/index>` didn't properly handle the transaction \"test mode\" flag.\n-  Fixed a bug (#2380) - :doc:`URI Routing <general/routing>` method ``fetch_method()`` returned 'index' if the requested method name matches its controller name.\n-  Fixed a bug (#2388) - :doc:`Email Library <libraries/email>` used to ignore attachment errors, resulting in broken emails being sent.\n-  Fixed a bug (#2498) - :doc:`Form Validation Library <libraries/form_validation>` rule **valid_base64** only checked characters instead of actual validity.\n-  Fixed a bug (#2425) - OCI8 :doc:`database <database/index>` driver method ``stored_procedure()`` didn't log an error unless **db_debug** was set to TRUE.\n-  Fixed a bug (#2490) - :doc:`Database Class <database/queries>` method ``query()`` returning boolean instead of a result object for PostgreSQL-specific *INSERT INTO ... RETURNING* statements.\n-  Fixed a bug (#249) - :doc:`Cache Library <libraries/caching>` didn't properly handle Memcache(d) configurations with missing options.\n-  Fixed a bug (#180) - :php:func:`config_item()` didn't take into account run-time configuration changes.\n-  Fixed a bug (#2551) - :doc:`Loader Library <libraries/loader>` method ``library()`` didn't properly check if a class that is being loaded already exists.\n-  Fixed a bug (#2560) - :doc:`Form Helper <helpers/form_helper>` function :php:func:`form_open()` set the 'method=\"post\"' attribute only if the passed attributes equaled an empty string.\n-  Fixed a bug (#2585) - :doc:`Query Builder <database/query_builder>` methods ``min()``, ``max()``, ``avg()``, ``sum()`` didn't escape field names.\n-  Fixed a bug (#2590) - :doc:`Common function <general/common_functions>` :php:func:`log_message()` didn't actually cache the ``CI_Log`` class instance.\n-  Fixed a bug (#2609) - :doc:`Common function <general/common_functions>` :php:func:`get_config()` optional argument was only effective on first function call. Also, it can now add items, in addition to updating existing items.\n-  Fixed a bug in the 'postgre' :doc:`database <database/index>` driver where the connection ID wasn't passed to ``pg_escape_string()``.\n-  Fixed a bug (#33) - Script execution was terminated when an invalid cookie key was encountered.\n-  Fixed a bug (#2691) - nested :doc:`database <database/index>` transactions could end in a deadlock when an error is encountered with *db_debug* set to TRUE.\n-  Fixed a bug (#2515) - ``_exception_handler()`` used to send the 200 \"OK\" HTTP status code and didn't stop script exection even on fatal errors.\n-  Fixed a bug - Redis :doc:`Caching <libraries/caching>` driver didn't handle connection failures properly.\n-  Fixed a bug (#2756) - :doc:`Database Class <database/index>` executed the MySQL-specific `SET SESSION sql_mode` query for all drivers when the 'stricton' option is set.\n-  Fixed a bug (#2579) - :doc:`Query Builder <database/query_builder>` \"no escape\" functionality didn't work properly with query cache.\n-  Fixed a bug (#2237) - :doc:`Parser Library <libraries/parser>` failed if the same tag pair is used more than once within a template.\n-  Fixed a bug (#2143) - :doc:`Form Validation Library <libraries/form_validation>` didn't check for rule groups named in a *controller/method* manner when trying to load from a config file.\n-  Fixed a bug (#2762) - :doc:`Hooks Class <general/hooks>` didn't properly check if the called class/function exists.\n-  Fixed a bug (#148) - :doc:`Input Library <libraries/input>` internal method ``_clean_input_data()`` assumed that it data is URL-encoded, stripping certain character sequences from it.\n-  Fixed a bug (#346) - with ``$config['global_xss_filtering']`` turned on, the ``$_GET``, ``$_POST``, ``$_COOKIE`` and ``$_SERVER`` superglobals were overwritten during initialization time, resulting in XSS filtering being either performed twice or there was no possible way to get the original data, even though options for this do exist.\n-  Fixed an edge case (#555) - :doc:`User Agent Library <libraries/user_agent>` reported an incorrect version Opera 10+ due to a non-standard user-agent string.\n-  Fixed a bug (#133) - :doc:`Text Helper <helpers/text_helper>` :php:func:`ascii_to_entities()` stripped the last character if it happens to be in the extended ASCII group.\n-  Fixed a bug (#2822) - ``fwrite()`` was used incorrectly throughout the whole framework, allowing incomplete writes when writing to a network stream and possibly a few other edge cases.\n-  Fixed a bug where :doc:`User Agent Library <libraries/user_agent>` methods ``accept_charset()`` and ``accept_lang()`` didn't properly parse HTTP headers that contain spaces.\n-  Fixed a bug where *default_controller* was called instad of triggering a 404 error if the current route is in a controller directory.\n-  Fixed a bug (#2737) - :doc:`XML-RPC Library <libraries/xmlrpc>` used objects as array keys, which triggered E_NOTICE messages.\n-  Fixed a bug (#2771) - :doc:`Security Library <libraries/security>` method ``xss_clean()`` didn't take into account HTML5 entities.\n-  Fixed a bug (#2856) - ODBC method ``affected_rows()`` passed an incorrect value to ``odbc_num_rows()``.\n-  Fixed a bug (#43) :doc:`Image Manipulation Library <libraries/image_lib>` method ``text_watermark()`` didn't properly determine watermark placement.\n-  Fixed a bug where :doc:`HTML Table Library <libraries/table>` ignored its *auto_heading* setting if headings were not already set.\n-  Fixed a bug (#2364) - :doc:`Pagination Library <libraries/pagination>` appended the query string (if used) multiple times when there are successive calls to ``create_links()`` with no ``initialize()`` in between them.\n-  Partially fixed a bug (#261) - UTF-8 class method ``clean_string()`` generating log messages and/or not producing the desired result due to an upstream bug in iconv.\n-  Fixed a bug where ``CI_Xmlrpcs::parseRequest()`` could fail if ``$HTTP_RAW_POST_DATA`` is not populated.\n-  Fixed a bug in :doc:`Zip Library <libraries/zip>` internal method ``_get_mod_time()`` where it was not parsing result returned by ``filemtime()``.\n-  Fixed a bug (#3161) - :doc:`Cache Library <libraries/caching>` methods `increment()`, `decrement()` didn't auto-create non-existent items when using redis and/or file storage.\n-  Fixed a bug (#3189) - :doc:`Parser Library <libraries/parser>` used double replacement on ``key->value`` pairs, exposing a potential template injection vulnerability.\n-  Fixed a bug (#3573) - :doc:`Email Library <libraries/email>` violated `RFC5321 <https://tools.ietf.org/rfc/rfc5321.txt>`_ by sending 'localhost.localdomain' as a hostname.\n-  Fixed a bug (#3572) - ``CI_Security::_remove_evil_attributes()`` failed for large-sized inputs due to *pcre.backtrack_limit* and didn't properly match HTML tags.\n\nVersion 2.2.3\n=============\n\nRelease Date: July 14, 2015\n\n-  Security\n\n   - Removed a fallback to ``mysql_escape_string()`` in the 'mysql' database driver (``escape_str()`` method) when there's no active database connection.\n\nVersion 2.2.2\n=============\n\nRelease Date: April 15, 2015\n\n-  General Changes\n\n   - Added HTTP \"Host\" header character validation to prevent cache poisoning attacks when *base_url* auto-detection is used.\n   - Added *FSCommand* and *seekSegmentTime* to the \"evil attributes\" list in ``CI_Security::xss_clean()``.\n\nBug fixes for 2.2.2\n-------------------\n\n-  Fixed a bug (#3665) - ``CI_Security::entity_decode()`` triggered warnings under some circumstances.\n\nVersion 2.2.1\n=============\n\nRelease Date: January 22, 2015\n\n-  General Changes\n\n   - Improved security in ``xss_clean()``.\n   - Updated timezones in :doc:`Date Helper <helpers/date_helper>`.\n\nBug fixes for 2.2.1\n-------------------\n\n-  Fixed a bug (#3094) - Internal method ``CI_Input::_clean_input_data()`` breaks encrypted session cookies.\n-  Fixed a bug (#2268) - :doc:`Security Library <libraries/security>` method ``xss_clean()`` didn't properly match JavaScript events.\n-  Fixed a bug (#3309) - :doc:`Security Library <libraries/security>` method ``xss_clean()`` used an overly-invasive pattern to strip JS event handlers.\n-  Fixed a bug (#2771) - :doc:`Security Library <libraries/security>` method ``xss_clean()`` didn't take into account HTML5 entities.\n-  Fixed a bug (#73) - :doc:`Security Library <libraries/security>` method ``sanitize_filename()`` could be tricked by an XSS attack.\n-  Fixed a bug (#2681) - :doc:`Security Library <libraries/security>` method ``entity_decode()`` used the ``PREG_REPLACE_EVAL`` flag, which is deprecated since PHP 5.5.\n-  Fixed a bug (#3302) - Internal function ``get_config()`` triggered an E_NOTICE message on PHP 5.6.\n-  Fixed a bug (#2508) - :doc:`Config Library <libraries/config>` didn't properly detect if the current request is via HTTPS.\n-  Fixed a bug (#3314) - SQLSRV :doc:`Database driver <database/index>`'s method ``count_all()`` didn't escape the supplied table name.\n-  Fixed a bug (#3404) - MySQLi :doc:`Database driver <database/index>`'s method ``escape_str()`` had a wrong fallback to ``mysql_escape_string()`` when there was no active connection.\n-  Fixed a bug in the :doc:`Session Library <libraries/sessions>` where session ID regeneration occurred during AJAX requests.\n\nVersion 2.2.0\n=============\n\nRelease Date: June 2, 2014\n\n-  General Changes\n\n   - Security: **Encrypt Library** method ``xor_encode()`` has been removed. The Encrypt Class now requires the Mcrypt extension to be installed.\n   - Security: The :doc:`Session Library <libraries/sessions>` now uses HMAC authentication instead of a simple MD5 checksum.\n\nBug fixes for 2.2.0\n-------------------\n\n-  Fixed an edge case (#2583) in the :doc:`Email Library <libraries/email>` where `Suhosin <http://www.hardened-php.net/suhosin/>` blocked messages sent via ``mail()`` due to trailing newspaces in headers.\n-  Fixed a bug (#696) - make ``oci_execute()`` calls inside ``num_rows()`` non-committing, since they are only there to reset which row is next in line for oci_fetch calls and thus don't need to be committed.\n-  Fixed a bug (#2689) - :doc:`Database Force <database/forge>` methods ``create_table()``, ``drop_table()`` and ``rename_table()`` produced broken SQL for tge 'sqlsrv' driver.\n-  Fixed a bug (#2427) - PDO :doc:`Database driver <database/index>` didn't properly check for query failures.\n-  Fixed a bug in the :doc:`Session Library <libraries/sessions>` where authentication was not performed for encrypted cookies.\n\nVersion 2.1.4\n=============\n\nRelease Date: July 8, 2013\n\n-  General Changes\n\n   - Improved security in ``xss_clean()``.\n\nBug fixes for 2.1.4\n-------------------\n\n-  Fixed a bug (#1936) - :doc:`Migration Library <libraries/migration>` method ``latest()`` had a typo when retrieving language values.\n-  Fixed a bug (#2021) - :doc:`Migration Library <libraries/migration>` configuration file was mistakenly using Windows style line feeds.\n-  Fixed a bug (#1273) - ``E_NOTICE`` being generated by :doc:`Query Builder <database/query_builder>`'s ``set_update_batch()`` method.\n-  Fixed a bug (#2337) - :doc:`Email Library <libraries/email>` method ``print_debugger()`` didn't apply ``htmlspecialchars()`` to headers.\n\nVersion 2.1.3\n=============\n\nRelease Date: October 8, 2012\n\n-  Core\n\n   - :doc:`Common function <general/common_functions>` ``is_loaded()`` now returns a reference.\n\nBug fixes for 2.1.3\n-------------------\n\n-  Fixed a bug (#1543) - File-based :doc:`Caching <libraries/caching>` method ``get_metadata()`` used a non-existent array key to look for the TTL value.\n-  Fixed a bug (#1314) - :doc:`Session Library <libraries/sessions>` method ``sess_destroy()`` didn't destroy the userdata array.\n-  Fixed a bug (#804) - :doc:`Profiler library <general/profiling>` was trying to handle objects as strings in some cases, resulting in *E_WARNING* messages being issued by ``htmlspecialchars()``.\n-  Fixed a bug (#1699) - :doc:`Migration Library <libraries/migration>` ignored the ``$config['migration_path']`` setting.\n-  Fixed a bug (#227) - :doc:`Input Library <libraries/input>` allowed unconditional spoofing of HTTP clients' IP addresses through the *HTTP_CLIENT_IP* header.\n-  Fixed a bug (#907) - :doc:`Input Library <libraries/input>` ignored *HTTP_X_CLUSTER_CLIENT_IP* and *HTTP_X_CLIENT_IP* headers when checking for proxies.\n-  Fixed a bug (#940) - ``csrf_verify()`` used to set the CSRF cookie while processing a POST request with no actual POST data, which resulted in validating a request that should be considered invalid.\n-  Fixed a bug (#499) - :doc:`Security Library <libraries/security>` where a CSRF cookie was created even if ``$config['csrf_protection']`` is set to FALSE.\n-  Fixed a bug (#1715) - :doc:`Input Library <libraries/input>` triggered ``csrf_verify()`` on CLI requests.\n-  Fixed a bug (#751) - :doc:`Query Builder <database/query_builder>` didn't properly handle cached field escaping overrides.\n-  Fixed a bug (#2004) - :doc:`Query Builder <database/query_builder>` didn't properly merge cached calls with non-cache ones.\n\nVersion 2.1.2\n=============\n\nRelease Date: June 29, 2012\n\n-  General Changes\n\n   -  Improved security in ``xss_clean()``.\n\nVersion 2.1.1\n=============\n\nRelease Date: June 12, 2012\n\n-  General Changes\n\n   -  Fixed support for docx, xlsx files in mimes.php.\n\n-  Libraries\n\n   -  Further improved MIME type detection in the :doc:`File Uploading Library <libraries/file_uploading>`.\n   -  Added support for IPv6 to the :doc:`Input Library <libraries/input>`.\n   -  Added support for the IP format parameter to the :doc:`Form Validation Library <libraries/form_validation>`.\n\n-  Helpers\n\n   -  ``url_title()`` performance and output improved. You can now use any string as the word delimiter, but 'dash' and 'underscore' are still supported.\n\nBug fixes for 2.1.1\n-------------------\n\n-  Fixed a bug (#697) - A wrong array key was used in the :doc:`File Uploading Library <libraries/file_uploading>` to check for mime-types.\n-  Fixed a bug - ``form_open()`` compared $action against ``site_url()`` instead of ``base_url()``.\n-  Fixed a bug - ``CI_Upload::_file_mime_type()`` could've failed if ``mime_content_type()`` is used for the detection and returns FALSE.\n-  Fixed a bug (#538) - Windows paths were ignored when using the :doc:`Image Manipulation Library <libraries/image_lib>` to create a new file.\n-  Fixed a bug - When database caching was enabled, $this->db->query() checked the cache before binding variables which resulted in cached queries never being found.\n-  Fixed a bug - CSRF cookie value was allowed to be any (non-empty) string before being written to the output, making code injection a risk.\n-  Fixed a bug (#726) - PDO put a 'dbname' argument in its connection string regardless of the database platform in use, which made it impossible to use SQLite.\n-  Fixed a bug - ``CI_DB_pdo_driver::num_rows()`` was not returning properly value with SELECT queries, cause it was relying on ``PDOStatement::rowCount()``.\n-  Fixed a bug (#1059) - ``CI_Image_lib::clear()`` was not correctly clearing all necessary object properties, namely width and height.\n\nVersion 2.1.0\n=============\n\nRelease Date: November 14, 2011\n\n-  General Changes\n\n   -  Callback validation rules can now accept parameters like any other\n      validation rule.\n   -  Added html_escape() to :doc:`Common\n      functions <general/common_functions>` to escape HTML output\n      for preventing XSS.\n\n-  Helpers\n\n   -  Added increment_string() to :doc:`String\n      Helper <helpers/string_helper>` to turn \"foo\" into \"foo-1\"\n      or \"foo-1\" into \"foo-2\".\n   -  Altered form helper - made action on form_open_multipart helper\n      function call optional. Fixes (#65)\n   -  url_title() will now trim extra dashes from beginning and end.\n   -  Improved speed of :doc:`String Helper <helpers/string_helper>`'s random_string() method\n\n-  Database\n\n   -  Added a `CUBRID <https://www.cubrid.org/>`_ driver to the :doc:`Database\n      Driver <database/index>`. Thanks to the CUBRID team for\n      supplying this patch.\n   -  Added a PDO driver to the :doc:`Database Driver <database/index>`.\n   -  Typecast limit and offset in the :doc:`Database\n      Driver <database/queries>` to integers to avoid possible\n      injection.\n   -  Added additional option 'none' for the optional third argument for\n      $this->db->like() in the :doc:`Database\n      Driver <database/query_builder>`.\n   -  Added $this->db->insert_batch() support to the OCI8 (Oracle) driver.\n   -  Added failover if the main connections in the config should fail\n\n-  Libraries\n\n   -  Changed ``$this->cart->insert()`` in the *Cart Library*\n      to return the Row ID if a single item was inserted successfully.\n   -  Added support to set an optional parameter in your callback rules\n      of validation using the :doc:`Form Validation\n      Library <libraries/form_validation>`.\n   -  Added a :doc:`Migration library <libraries/migration>` to assist with applying\n      incremental updates to your database schema.\n   -  Driver children can be located in any package path.\n   -  Added max_filename_increment config setting for Upload library.\n   -  Added ``is_unique`` to the :doc:`Form Validation library <libraries/form_validation>`.\n   -  Added $config['use_page_numbers'] to the :doc:`Pagination library <libraries/pagination>`, which enables real page numbers in the URI.\n   -  Added TLS and SSL Encryption for SMTP.\n\n-  Core\n\n   -  Changed private functions in CI_URI to protected so MY_URI can\n      override them.\n   -  Removed CI_CORE boolean constant from CodeIgniter.php (no longer Reactor and Core versions).\n\nBug fixes for 2.1.0\n-------------------\n\n-  Fixed #378 Robots identified as regular browsers by the User Agent\n   class.\n-  If a config class was loaded first then a library with the same name\n   is loaded, the config would be ignored.\n-  Fixed a bug (Reactor #19) where 1) the 404_override route was being\n   ignored in some cases, and 2) auto-loaded libraries were not\n   available to the 404_override controller when a controller existed\n   but the requested method did not.\n-  Fixed a bug (Reactor #89) where MySQL export would fail if the table\n   had hyphens or other non alphanumeric/underscore characters.\n-  Fixed a bug (#105) that stopped query errors from being logged unless database debugging was enabled\n-  Fixed a bug (#160) - Removed unneeded array copy in the file cache\n   driver.\n-  Fixed a bug (#150) - field_data() now correctly returns column\n   length.\n-  Fixed a bug (#8) - load_class() now looks for core classes in\n   APPPATH first, allowing them to be replaced.\n-  Fixed a bug (#24) - ODBC database driver called incorrect parent in __construct().\n-  Fixed a bug (#85) - OCI8 (Oracle) database escape_str() function did not escape correct.\n-  Fixed a bug (#344) - Using schema found in :doc:`Saving Session Data to a Database <libraries/sessions>`, system would throw error \"user_data does not have a default value\" when deleting then creating a session.\n-  Fixed a bug (#112) - OCI8 (Oracle) driver didn't pass the configured database character set when connecting.\n-  Fixed a bug (#182) - OCI8 (Oracle) driver used to re-execute the statement whenever num_rows() is called.\n-  Fixed a bug (#82) - WHERE clause field names in the DB update_string() method were not escaped, resulting in failed queries in some cases.\n-  Fixed a bug (#89) - Fix a variable type mismatch in DB display_error() where an array is expected, but a string could be set instead.\n-  Fixed a bug (#467) - Suppress warnings generated from get_magic_quotes_gpc() (deprecated in PHP 5.4)\n-  Fixed a bug (#484) - First time _csrf_set_hash() is called, hash is never set to the cookie (in Security.php).\n-  Fixed a bug (#60) - Added _file_mime_type() method to the :doc:`File Uploading Library <libraries/file_uploading>` in order to fix a possible MIME-type injection.\n-  Fixed a bug (#537) - Support for all wav type in browser.\n-  Fixed a bug (#576) - Using ini_get() function to detect if apc is enabled or not.\n-  Fixed invalid date time format in :doc:`Date helper <helpers/date_helper>` and :doc:`XMLRPC library <libraries/xmlrpc>`.\n-  Fixed a bug (#200) - MySQL queries would be malformed after calling db->count_all() then db->get().\n\nVersion 2.0.3\n=============\n\nRelease Date: August 20, 2011\n\n-  Security\n\n   -  An improvement was made to the MySQL and MySQLi drivers to prevent\n      exposing a potential vector for SQL injection on sites using\n      multi-byte character sets in the database client connection.\n      An incompatibility in PHP versions < 5.2.3 and MySQL < 5.0.7 with\n      *mysql_set_charset()* creates a situation where using multi-byte\n      character sets on these environments may potentially expose a SQL\n      injection attack vector. Latin-1, UTF-8, and other \"low ASCII\"\n      character sets are unaffected on all environments.\n\n      If you are running or considering running a multi-byte character\n      set for your database connection, please pay close attention to\n      the server environment you are deploying on to ensure you are not\n      vulnerable.\n\n-  General Changes\n\n   -  Fixed a bug where there was a misspelling within a code comment in\n      the index.php file.\n   -  Added Session Class userdata to the output profiler. Additionally,\n      added a show/hide toggle on HTTP Headers, Session Data and Config\n      Variables.\n   -  Removed internal usage of the EXT constant.\n   -  Visual updates to the welcome_message view file and default error\n      templates. Thanks to `danijelb <https://bitbucket.org/danijelb>`_\n      for the pull request.\n   -  Added insert_batch() function to the PostgreSQL database driver.\n      Thanks to epallerols for the patch.\n   -  Added \"application/x-csv\" to mimes.php.\n   -  Fixed a bug where :doc:`Email library <libraries/email>`\n      attachments with a \".\" in the name would using invalid MIME-types.\n\n-  Helpers\n\n   -  Added an optional third parameter to heading() which allows adding\n      html attributes to the rendered heading tag.\n   -  form_open() now only adds a hidden (Cross-site Reference Forgery)\n      protection field when the form's action is internal and is set to\n      the post method. (Reactor #165)\n   -  Re-worked plural() and singular() functions in the :doc:`Inflector\n      helper <helpers/inflector_helper>` to support considerably\n      more words.\n\n-  Libraries\n\n   -  Altered Session to use a longer match against the user_agent\n      string. See upgrade notes if using database sessions.\n   -  Added $this->db->set_dbprefix() to the :doc:`Database\n      Driver <database/queries>`.\n   -  Changed ``$this->cart->insert()`` in the *Cart Library*\n      to return the Row ID if a single item was inserted successfully.\n   -  Added $this->load->get_var() to the :doc:`Loader\n      library <libraries/loader>` to retrieve global vars set with\n      $this->load->view() and $this->load->vars().\n   -  Changed $this->db->having() to insert quotes using escape() rather\n      than escape_str().\n\nBug fixes for 2.0.3\n-------------------\n\n-  Added ENVIRONMENT to reserved constants. (Reactor #196)\n-  Changed server check to ensure SCRIPT_NAME is defined. (Reactor #57)\n-  Removed APPPATH.'third_party' from the packages autoloader to negate\n   needless file stats if no packages exist or if the developer does not\n   load any other packages by default.\n-  Fixed a bug (Reactor #231) where Sessions Library database table\n   example SQL did not contain an index on last_activity. See :doc:`Upgrade\n   Notes <installation/upgrade_203>`.\n-  Fixed a bug (Reactor #229) where the Sessions Library example SQL in\n   the documentation contained incorrect SQL.\n-  Fixed a bug (Core #340) where when passing in the second parameter to\n   $this->db->select(), column names in subsequent queries would not be\n   properly escaped.\n-  Fixed issue #199 - Attributes passed as string does not include a\n   space between it and the opening tag.\n-  Fixed a bug where the method ``$this->cart->total_items()`` from\n   *Cart Library* now returns the sum of the quantity\n   of all items in the cart instead of your total count.\n-  Fixed a bug where not setting 'null' when adding fields in db_forge\n   for mysql and mysqli drivers would default to NULL instead of NOT\n   NULL as the docs suggest.\n-  Fixed a bug where using $this->db->select_max(),\n   $this->db->select_min(), etc could throw notices. Thanks to w43l for\n   the patch.\n-  Replace checks for STDIN with php_sapi_name() == 'cli' which on the\n   whole is more reliable. This should get parameters in crontab\n   working.\n\nVersion 2.0.2\n=============\n\nRelease Date: April 7, 2011\nHg Tag: v2.0.2\n\n-  General changes\n\n   -  The :doc:`Security library <./libraries/security>` was moved to\n      the core and is now loaded automatically. Please remove your\n      loading calls.\n   -  The CI_SHA class is now deprecated. All supported versions of PHP\n      provide a sha1() function.\n   -  constants.php will now be loaded from the environment folder if\n      available.\n   -  Added language key error logging\n   -  Made Environment Support optional. Comment out or delete the\n      constant to stop environment checks.\n   -  Added Environment Support for Hooks.\n   -  Added CI\\_ Prefix to the :doc:`Cache driver <libraries/caching>`.\n   -  Added :doc:`CLI usage <./general/cli>` documentation.\n\n-  Helpers\n\n   -  Removed the previously deprecated ``dohash()`` from the :doc:`Security\n      helper <./helpers/security_helper>`; use ``do_hash()`` instead.\n   -  Changed the 'plural' function so that it doesn't ruin the\n      captalization of your string. It also take into consideration\n      acronyms which are all caps.\n\n-  Database\n\n   -  $this->db->count_all_results() will now return an integer\n      instead of a string.\n\nBug fixes for 2.0.2\n-------------------\n\n-  Fixed a bug (Reactor #145) where the Output Library had\n   parse_exec_vars set to protected.\n-  Fixed a bug (Reactor #80) where is_really_writable would create an\n   empty file when on Windows or with safe_mode enabled.\n-  Fixed various bugs with User Guide.\n-  Added is_cli_request() method to documentation for :doc:`Input\n   class <libraries/input>`.\n-  Added form_validation_lang entries for decimal, less_than and\n   greater_than.\n-  Fixed issue #153 Escape Str Bug in MSSQL driver.\n-  Fixed issue #172 Google Chrome 11 posts incorrectly when action is empty.\n\nVersion 2.0.1\n=============\n\nRelease Date: March 15, 2011\nHg Tag: v2.0.1\n\n-  General changes\n\n   -  Added $config['cookie_secure'] to the config file to allow\n      requiring a secure (HTTPS) in order to set cookies.\n   -  Added the constant CI_CORE to help differentiate between Core:\n      TRUE and Reactor: FALSE.\n   -  Added an ENVIRONMENT constant in index.php, which affects PHP\n      error reporting settings, and optionally, which configuration\n      files are loaded (see below). Read more on the :doc:`Handling\n      Environments <general/environments>` page.\n   -  Added support for\n      :ref:`environment-specific <config-environments>`\n      configuration files.\n\n-  Libraries\n\n   -  Added decimal, less_than and greater_than rules to the :doc:`Form\n      validation Class <libraries/form_validation>`.\n   -  :doc:`Input Class <libraries/input>` methods post() and get()\n      will now return a full array if the first argument is not\n      provided.\n   -  Secure cookies can now be made with the set_cookie() helper and\n      :doc:`Input Class <libraries/input>` method.\n   -  Added set_content_type() to :doc:`Output\n      Class <libraries/output>` to set the output Content-Type\n      HTTP header based on a MIME Type or a config/mimes.php array key.\n   -  :doc:`Output Class <libraries/output>` will now support method\n      chaining.\n\n-  Helpers\n\n   -  Changed the logic for form_open() in :doc:`Form\n      helper <helpers/form_helper>`. If no value is passed it will\n      submit to the current URL.\n\nBug fixes for 2.0.1\n-------------------\n\n-  CLI requests can now be run from any folder, not just when CD'ed next\n   to index.php.\n-  Fixed issue #41: Added audio/mp3 mime type to mp3.\n-  Fixed a bug (Core #329) where the file caching driver referenced the\n   incorrect cache directory.\n-  Fixed a bug (Reactor #69) where the SHA1 library was named\n   incorrectly.\n\n.. _2.0.0-changelog:\n\nVersion 2.0.0\n=============\n\nRelease Date: January 28, 2011\nHg Tag: v2.0.0\n\n-  General changes\n\n   -  PHP 4 support is removed. CodeIgniter now requires PHP 5.1.6.\n   -  Scaffolding, having been deprecated for a number of versions, has\n      been removed.\n   -  Plugins have been removed, in favor of Helpers. The CAPTCHA plugin\n      has been converted to a Helper and\n      :doc:`documented <./helpers/captcha_helper>`. The JavaScript\n      calendar plugin was removed due to the ready availability of great\n      JavaScript calendars, particularly with jQuery.\n   -  Added new special Library type:\n      :doc:`Drivers <./general/drivers>`.\n   -  Added full query-string support. See the config file for details.\n   -  Moved the application folder outside of the system folder.\n   -  Moved system/cache and system/logs directories to the application\n      directory.\n   -  Added routing overrides to the main index.php file, enabling the\n      normal routing to be overridden on a per \"index\" file basis.\n   -  Added the ability to set config values (or override config values)\n      directly from data set in the main index.php file. This allows a\n      single application to be used with multiple front controllers,\n      each having its own config values.\n   -  Added $config['directory_trigger'] to the config file so that a\n      controller sub-directory can be specified when running _GET\n      strings instead of URI segments.\n   -  Added ability to set \"Package\" paths - specific paths where the\n      Loader and Config classes should try to look first for a requested\n      file. This allows distribution of sub-applications with their own\n      libraries, models, config files, etc. in a single \"package\"\n      directory. See the :doc:`Loader class <libraries/loader>`\n      documentation for more details.\n   -  In-development code is now hosted at BitBucket .\n   -  Removed the deprecated Validation Class.\n   -  Added CI\\_ Prefix to all core classes.\n   -  Package paths can now be set in application/config/autoload.php.\n   -  :doc:`Upload library <libraries/file_uploading>` file_name can\n      now be set without an extension, the extension will be taken from\n      the uploaded file instead of the given name.\n   -  In :doc:`Database Forge <database/forge>` the name can be omitted\n      from $this->dbforge->modify_column()'s 2nd param if you aren't\n      changing the name.\n   -  $config['base_url'] is now empty by default and will guess what\n      it should be.\n   -  Enabled full Command Line Interface compatibility with\n      config['uri_protocol'] = 'CLI';.\n\n-  Libraries\n\n   -  Added a :doc:`Cache driver <libraries/caching>` with APC,\n      memcached, and file-based support.\n   -  Added $prefix, $suffix and $first_url properties to :doc:`Pagination\n      library <./libraries/pagination>`.\n   -  Added the ability to suppress first, previous, next, last, and\n      page links by setting their values to FALSE in the :doc:`Pagination\n      library <./libraries/pagination>`.\n   -  Added :doc:`Security library <./libraries/security>`, which now\n      contains the xss_clean function, filename_security function and\n      other security related functions.\n   -  Added CSRF (Cross-site Reference Forgery) protection to the\n      :doc:`Security library <./libraries/security>`.\n   -  Added $parse_exec_vars property to Output library.\n   -  Added ability to enable / disable individual sections of the\n      :doc:`Profiler <general/profiling>`\n   -  Added a wildcard option $config['allowed_types'] = '\\*' to the\n      :doc:`File Uploading Class <./libraries/file_uploading>`.\n   -  Added an 'object' config variable to the XML-RPC Server library so\n      that one can specify the object to look for requested methods,\n      instead of assuming it is in the $CI superobject.\n   -  Added \"is_object\" into the list of unit tests capable of being\n      run.\n   -  Table library will generate an empty cell with a blank string, or\n      NULL value.\n   -  Added ability to set tag attributes for individual cells in the\n      Table library\n   -  Added a parse_string() method to the :doc:`Parser\n      Class <libraries/parser>`.\n   -  Added HTTP headers and Config information to the\n      :doc:`Profiler <general/profiling>` output.\n   -  Added Chrome and Flock to the list of detectable browsers by\n      browser() in the :doc:`User Agent Class <libraries/user_agent>`.\n   -  The :doc:`Unit Test Class <libraries/unit_testing>` now has an\n      optional \"notes\" field available to it, and allows for discrete\n      display of test result items using\n      $this->unit->set_test_items().\n   -  Added a $xss_clean class variable to the XMLRPC library, enabling\n      control over the use of the Security library's xss_clean()\n      method.\n   -  Added a download() method to the :doc:`FTP\n      library <libraries/ftp>`\n   -  Changed do_xss_clean() to return FALSE if the uploaded file\n      fails XSS checks.\n   -  Added stripslashes() and trim()ing of double quotes from $_FILES\n      type value to standardize input in Upload library.\n   -  Added a second parameter (boolean) to\n      $this->zip->read_dir('/path/to/directory', FALSE) to remove the\n      preceding trail of empty folders when creating a Zip archive. This\n      example would contain a zip with \"directory\" and all of its\n      contents.\n   -  Added ability in the Image Library to handle PNG transparency for\n      resize operations when using the GD lib.\n   -  Modified the Session class to prevent use if no encryption key is\n      set in the config file.\n   -  Added a new config item to the Session class\n      sess_expire_on_close to allow sessions to auto-expire when the\n      browser window is closed.\n   -  Improved performance of the Encryption library on servers where\n      Mcrypt is available.\n   -  Changed the default encryption mode in the Encryption library to\n      CBC.\n   -  Added an encode_from_legacy() method to provide a way to\n      transition encrypted data from CodeIgniter 1.x to CodeIgniter 2.x.\n      Please see the :doc:`upgrade\n      instructions <./installation/upgrade_200>` for details.\n   -  Altered Form_Validation library to allow for method chaining on\n      set_rules(), set_message() and set_error_delimiters()\n      functions.\n   -  Altered Email Library to allow for method chaining.\n   -  Added request_headers(), get_request_header() and\n      is_ajax_request() to the input class.\n   -  Altered :doc:`User agent library <libraries/user_agent>` so that\n      is_browser(), is_mobile() and is_robot() can optionally check\n      for a specific browser or mobile device.\n   -  Altered :doc:`Input library <libraries/input>` so that post() and\n      get() will return all POST and GET items (respectively) if there\n      are no parameters passed in.\n\n-  Database\n\n   -  :doc:`database configuration <./database/configuration>`.\n   -  Added autoinit value to :doc:`database\n      configuration <./database/configuration>`.\n   -  Added stricton value to :doc:`database\n      configuration <./database/configuration>`.\n   -  Added database_exists() to the :doc:`Database Utilities\n      Class <database/utilities>`.\n   -  Semantic change to db->version() function to allow a list of\n      exceptions for databases with functions to return version string\n      instead of specially formed SQL queries. Currently this list only\n      includes Oracle and SQLite.\n   -  Fixed a bug where driver specific table identifier protection\n      could lead to malformed queries in the field_data() functions.\n   -  Fixed a bug where an undefined class variable was referenced in\n      database drivers.\n   -  Modified the database errors to show the filename and line number\n      of the problematic query.\n   -  Removed the following deprecated functions: orwhere, orlike,\n      groupby, orhaving, orderby, getwhere.\n   -  Removed deprecated _drop_database() and _create_database()\n      functions from the db utility drivers.\n   -  Improved dbforge create_table() function for the Postgres driver.\n\n-  Helpers\n\n   -  Added convert_accented_characters() function to :doc:`text\n      helper <./helpers/text_helper>`.\n   -  Added accept-charset to the list of inserted attributes of\n      form_open() in the :doc:`Form Helper <helpers/form_helper>`.\n   -  Deprecated the ``dohash()`` function in favour of ``do_hash()`` for\n      naming consistency.\n   -  Non-backwards compatible change made to get_dir_file_info() in\n      the :doc:`File Helper <helpers/file_helper>`. No longer recurses\n      by default so as to encourage responsible use (this function can\n      cause server performance issues when used without caution).\n   -  Modified the second parameter of directory_map() in the\n      :doc:`Directory Helper <helpers/directory_helper>` to accept an\n      integer to specify recursion depth.\n   -  Modified delete_files() in the :doc:`File\n      Helper <helpers/file_helper>` to return FALSE on failure.\n   -  Added an optional second parameter to byte_format() in the\n      :doc:`Number Helper <helpers/number_helper>` to allow for decimal\n      precision.\n   -  Added alpha, and sha1 string types to random_string() in the\n      :doc:`String Helper <helpers/string_helper>`.\n   -  Modified prep_url() so as to not prepend \\http:// if the supplied\n      string already has a scheme.\n   -  Modified get_file_info in the file helper, changing filectime()\n      to filemtime() for dates.\n   -  Modified ``smiley_js()`` to add optional third parameter to return\n      only the javascript with no script tags.\n   -  The img() function of the :doc:`HTML\n      helper <./helpers/html_helper>` will now generate an empty\n      string as an alt attribute if one is not provided.\n   -  If CSRF is enabled in the application config file, form_open()\n      will automatically insert it as a hidden field.\n   -  Added sanitize_filename() into the :doc:`Security\n      helper <./helpers/security_helper>`.\n   -  Added ellipsize() to the :doc:`Text\n      Helper <./helpers/text_helper>`\n   -  Added elements() to the :doc:`Array\n      Helper <./helpers/array_helper>`\n\n-  Other Changes\n\n   -  Added an optional second parameter to show_404() to disable\n      logging.\n   -  Updated loader to automatically apply the sub-class prefix as an\n      option when loading classes. Class names can be prefixed with the\n      standard \"CI\\_\" or the same prefix as the subclass prefix, or no\n      prefix at all.\n   -  Increased randomness with is_really_writable() to avoid file\n      collisions when hundreds or thousands of requests occur at once.\n   -  Switched some DIR_WRITE_MODE constant uses to FILE_WRITE_MODE\n      where files and not directories are being operated on.\n   -  get_mime_by_extension() is now case insensitive.\n   -  Added \"default\" to the list :doc:`Reserved\n      Names <general/reserved_names>`.\n   -  Added 'application/x-msdownload' for .exe files and\n      'application/x-gzip-compressed' for .tgz files to\n      config/mimes.php.\n   -  Updated the output library to no longer compress output or send\n      content-length headers if the server runs with\n      zlib.output_compression enabled.\n   -  Eliminated a call to is_really_writable() on each request unless\n      it is really needed (Output caching)\n   -  Documented append_output() in the :doc:`Output\n      Class <libraries/output>`.\n   -  Documented a second argument in the decode() function for the\n      **Encrypt Class**.\n   -  Documented db->close().\n   -  Updated the router to support a default route with any number of\n      segments.\n   -  Moved _remove_invisible_characters() function from the\n      :doc:`Security Library <libraries/security>` to :doc:`common\n      functions. <general/common_functions>`\n   -  Added audio/mpeg3 as a valid mime type for MP3.\n\nBug fixes for 2.0.0\n-------------------\n\n-  Fixed a bug where you could not change the User-Agent when sending\n   email.\n-  Fixed a bug where the Output class would send incorrect cached output\n   for controllers implementing their own _output() method.\n-  Fixed a bug where a failed query would not have a saved query\n   execution time causing errors in the Profiler\n-  Fixed a bug that was writing log entries when multiple identical\n   helpers and plugins were loaded.\n-  Fixed assorted user guide typos or examples (#10693, #8951, #7825,\n   #8660, #7883, #6771, #10656).\n-  Fixed a language key in the profiler: \"profiler_no_memory_usage\"\n   to \"profiler_no_memory\".\n-  Fixed an error in the Zip library that didn't allow downloading on\n   PHP 4 servers.\n-  Fixed a bug in the Form Validation library where fields passed as\n   rule parameters were not being translated (#9132)\n-  Modified inflector helper to properly pluralize words that end in\n   'ch' or 'sh'\n-  Fixed a bug in xss_clean() that was not allowing hyphens in query\n   strings of submitted URLs.\n-  Fixed bugs in get_dir_file_info() and get_file_info() in the\n   File Helper with recursion, and file paths on Windows.\n-  Fixed a bug where Active Record override parameter would not let you\n   disable Active Record if it was enabled in your database config file.\n-  Fixed a bug in reduce_double_slashes() in the String Helper to\n   properly remove duplicate leading slashes (#7585)\n-  Fixed a bug in values_parsing() of the XML-RPC library which\n   prevented NULL variables typed as 'string' from being handled\n   properly.\n-  Fixed a bug were form_open_multipart() didn't accept string\n   attribute arguments (#10930).\n-  Fixed a bug (#10470) where get_mime_by_extension() was case\n   sensitive.\n-  Fixed a bug where some error messages for the SQLite and Oracle\n   drivers would not display.\n-  Fixed a bug where files created with the Zip Library would result in\n   file creation dates of 1980.\n-  Fixed a bug in the Session library that would result in PHP error\n   when attempting to store values with objects.\n-  Fixed a bug where extending the Controller class would result in a\n   fatal PHP error.\n-  Fixed a PHP Strict Standards Error in the index.php file.\n-  Fixed a bug where getimagesize() was being needlessly checked on\n   non-image files in is_allowed_type().\n-  Fixed a bug in the Encryption library where an empty key was not\n   triggering an error.\n-  Fixed a bug in the Email library where CC and BCC recipients were not\n   reset when using the clear() method (#109).\n-  Fixed a bug in the URL Helper where prep_url() could cause a PHP\n   error on PHP versions < 5.1.2.\n-  Added a log message in core/output if the cache directory config\n   value was not found.\n-  Fixed a bug where multiple libraries could not be loaded by passing\n   an array to load->library()\n-  Fixed a bug in the html helper where too much white space was\n   rendered between the src and alt tags in the img() function.\n-  Fixed a bug in the profilers _compile_queries() function.\n-  Fixed a bug in the date helper where the DATE_ISO8601 variable was\n   returning an incorrectly formatted date string.\n\nVersion 1.7.2\n=============\n\nRelease Date: September 11, 2009\nHg Tag: v1.7.2\n\n-  Libraries\n\n   -  Added a new *Cart Class*.\n   -  Added the ability to pass $config['file_name'] for the :doc:`File\n      Uploading Class <libraries/file_uploading>` and rename the\n      uploaded file.\n   -  Changed order of listed user-agents so Safari would more\n      accurately report itself. (#6844)\n\n-  Database\n\n   -  Switched from using gettype() in escape() to is\\_* methods, since\n      future PHP versions might change its output.\n   -  Updated all database drivers to handle arrays in escape_str()\n   -  Added escape_like_str() method for escaping strings to be used\n      in LIKE conditions\n   -  Updated Active Record to utilize the new LIKE escaping mechanism.\n   -  Added reconnect() method to DB drivers to try to keep alive /\n      reestablish a connection after a long idle.\n   -  Modified MSSQL driver to use mssql_get_last_message() for error\n      messages.\n\n-  Helpers\n\n   -  Added form_multiselect() to the :doc:`Form\n      helper <helpers/form_helper>`.\n   -  Modified form_hidden() in the :doc:`Form\n      helper <helpers/form_helper>` to accept multi-dimensional\n      arrays.\n   -  Modified ``form_prep()`` in the :doc:`Form\n      helper <helpers/form_helper>` to keep track of prepped\n      fields to avoid multiple prep/mutation from subsequent calls which\n      can occur when using Form Validation and form helper functions to\n      output form fields.\n   -  Modified directory_map() in the :doc:`Directory\n      helper <helpers/directory_helper>` to allow the inclusion of\n      hidden files, and to return FALSE on failure to read directory.\n   -  Modified the *Smiley helper* to work\n      with multiple fields and insert the smiley at the last known\n      cursor position.\n\n-  General\n\n   -  Compatible with PHP 5.3.0.\n   -  Modified :doc:`show_error() <general/errors>` to allow sending\n      of HTTP server response codes.\n   -  Modified :doc:`show_404() <general/errors>` to send 404 status\n      code, removing non-CGI compatible header() statement from\n      error_404.php template.\n   -  Added set_status_header() to the :doc:`Common\n      functions <general/common_functions>` to allow use when the\n      Output class is unavailable.\n   -  Added is_php() to :doc:`Common\n      functions <general/common_functions>` to facilitate PHP\n      version comparisons.\n   -  Added 2 CodeIgniter \"cheatsheets\" (thanks to DesignFellow.com for\n      this contribution).\n\nBug fixes for 1.7.2\n-------------------\n\n-  Fixed assorted user guide typos or examples (#6743, #7214, #7516,\n   #7287, #7852, #8224, #8324, #8349).\n-  Fixed a bug in the Form Validation library where multiple callbacks\n   weren't working (#6110)\n-  doctype helper default value was missing a \"1\".\n-  Fixed a bug in the language class when outputting an error for an\n   unfound file.\n-  Fixed a bug in the Calendar library where the shortname was output\n   for \"May\".\n-  Fixed a bug with ORIG_PATH_INFO that was allowing URIs of just a\n   slash through.\n-  Fixed a fatal error in the Oracle and ODBC drivers (#6752)\n-  Fixed a bug where xml_from_result() was checking for a nonexistent\n   method.\n-  Fixed a bug where Database Forge's add_column and modify_column\n   were not looping through when sent multiple fields.\n-  Fixed a bug where the File Helper was using '/' instead of the\n   DIRECTORY_SEPARATOR constant.\n-  Fixed a bug to prevent PHP errors when attempting to use sendmail on\n   servers that have manually disabled the PHP popen() function.\n-  Fixed a bug that would cause PHP errors in XML-RPC data if the PHP\n   data type did not match the specified XML-RPC type.\n-  Fixed a bug in the XML-RPC class with parsing dateTime.iso8601 data\n   types.\n-  Fixed a case sensitive string replacement in xss_clean()\n-  Fixed a bug in form_textarea() where form data was not prepped\n   correctly.\n-  Fixed a bug in ``form_prep()`` causing it to not preserve entities in\n   the user's original input when called back into a form element\n-  Fixed a bug in _protect_identifiers() where the swap prefix\n   ($swap_pre) was not being observed.\n-  Fixed a bug where the 400 status header sent with the 'disallowed URI\n   characters' was not compatible with CGI environments.\n-  Fixed a bug in the typography class where heading tags could have\n   paragraph tags inserted when using auto_typography().\n\nVersion 1.7.1\n=============\n\nRelease Date: February 10, 2009\nHg Tag: 1.7.1\n\n-  Libraries\n\n   -  Fixed an arbitrary script execution security flaw (#6068) in the\n      Form Validation library (thanks to hkk)\n   -  Changed default current page indicator in the Pagination library\n      to use <strong> instead of <b>\n   -  A \"HTTP/1.1 400 Bad Request\" header is now sent when disallowed\n      characters are encountered.\n   -  Added <big>, <small>, <q>, and <tt> to the Typography parser's\n      inline elements.\n   -  Added more accurate error reporting for the Email library when\n      using sendmail.\n   -  Removed a strict type check from the rotate() function of the\n      :doc:`Image Manipulation Class <libraries/image_lib>`.\n   -  Added enhanced error checking in file saving in the Image library\n      when using the GD lib.\n   -  Added an additional newline between multipart email headers and\n      the MIME message text for better compatibility with a variety of\n      MUAs.\n   -  Made modest improvements to efficiency and accuracy of\n      explode_name() in the Image lib.\n\n-  Database\n\n   -  Added where_in to the list of expected arguments received by\n      delete().\n\n-  Helpers\n\n   -  Added the ability to have optgroups in form_dropdown() within the\n      :doc:`form helper <helpers/form_helper>`.\n   -  Added a doctype() function to the :doc:`HTML\n      helper <helpers/html_helper>`.\n   -  Added ability to force lowercase for url_title() in the :doc:`URL\n      helper <helpers/url_helper>`.\n   -  Changed the default \"type\" of form_button() to \"button\" from\n      \"submit\" in the :doc:`form helper <helpers/form_helper>`.\n   -  Changed redirect() in the URL helper to allow redirections to URLs\n      outside of the CI site.\n   -  Updated get_cookie() to try to fetch the cookie using the global\n      cookie prefix if the requested cookie name doesn't exist.\n\n-  Other Changes\n\n   -  Improved security in xss_clean() to help prevent attacks\n      targeting Internet Explorer.\n   -  Added 'application/msexcel' to config/mimes.php for .xls files.\n   -  Added 'proxy_ips' config item to whitelist reverse proxy servers\n      from which to trust the HTTP_X_FORWARDED_FOR header to to\n      determine the visitor's IP address.\n   -  Improved accuracy of Upload::is_allowed_filetype() for images\n      (#6715)\n\nBug fixes for 1.7.1\n-------------------\n\n-  Database\n\n   -  Fixed a bug when doing 'random' on order_by() (#5706).\n   -  Fixed a bug where adding a primary key through Forge could fail\n      (#5731).\n   -  Fixed a bug when using DB cache on multiple databases (#5737).\n   -  Fixed a bug where TRUNCATE was not considered a \"write\" query\n      (#6619).\n   -  Fixed a bug where csv_from_result() was checking for a\n      nonexistent method.\n   -  Fixed a bug _protect_identifiers() where it was improperly\n      removing all pipe symbols from items\n\n-  Fixed assorted user guide typos or examples (#5998, #6093, #6259,\n   #6339, #6432, #6521).\n-  Fixed a bug in the MySQLi driver when no port is specified\n-  Fixed a bug (#5702), in which the field label was not being fetched\n   properly, when \"matching\" one field to another.\n-  Fixed a bug in which identifers were not being escaped properly when\n   reserved characters were used.\n-  Fixed a bug with the regular expression used to protect submitted\n   paragraph tags in auto typography.\n-  Fixed a bug where double dashes within tag attributes were being\n   converted to em dash entities.\n-  Fixed a bug where double spaces within tag attributes were being\n   converted to non-breaking space entities.\n-  Fixed some accuracy issues with curly quotes in\n   Typography::format_characters()\n-  Changed a few docblock comments to reflect actual return values.\n-  Fixed a bug with high ascii characters in subject and from email\n   headers.\n-  Fixed a bug in xss_clean() where whitespace following a validated\n   character entity would not be preserved.\n-  Fixed a bug where HTML comments and <pre> tags were being parsed in\n   Typography::auto_typography().\n-  Fixed a bug with non-breaking space cleanup in\n   Typography::auto_typography().\n-  Fixed a bug in database escaping where a compound statement (ie:\n   SUM()) wasn't handled correctly with database prefixes.\n-  Fixed a bug when an opening quote is preceded by a paragraph tag and\n   immediately followed by another tag.\n-  Fixed a bug in the Text Helper affecting some locales where\n   word_censor() would not work on words beginning or ending with an\n   accented character.\n-  Fixed a bug in the Text Helper character limiter where the provided\n   limit intersects the last word of the string.\n-  Fixed a bug (#6342) with plural() in the Inflection helper with words\n   ending in \"y\".\n-  Fixed bug (#6517) where Routed URI segments returned by\n   URI::rsegment() method were incorrect for the default controller.\n-  Fixed a bug (#6706) in the Security Helper where xss_clean() was\n   using a deprecated second argument.\n-  Fixed a bug in the URL helper url_title() function where trailing\n   periods were allowed at the end of a URL.\n-  Fixed a bug (#6669) in the Email class when CRLF's are used for the\n   newline character with headers when used with the \"mail\" protocol.\n-  Fixed a bug (#6500) where URI::A_filter_uri() was exit()ing an\n   error instead of using show_error().\n-  Fixed a bug (#6592) in the File Helper where get_dir_file_info()\n   where recursion was not occurring properly.\n-  Tweaked Typography::auto_typography() for some edge-cases.\n\nVersion 1.7\n===========\n\nRelease Date: October 23, 2008\nHg Tag: 1.7.0\n\n-  Libraries\n\n   -  Added a new :doc:`Form Validation\n      Class <libraries/form_validation>`. It simplifies setting\n      rules and field names, supports arrays as field names, allows\n      groups of validation rules to be saved in a config file, and adds\n      some helper functions for use in view files. **Please note that\n      the old Validation class is now deprecated**. We will leave it in\n      the library folder for some time so that existing applications\n      that use it will not break, but you are encouraged to migrate to\n      the new version.\n   -  Updated the :doc:`Sessions class <libraries/sessions>` so that\n      any custom data being saved gets stored to a database rather than\n      the session cookie (assuming you are using a database to store\n      session data), permitting much more data to be saved.\n   -  Added the ability to store libraries in subdirectories within\n      either the main \"libraries\" or the local application \"libraries\"\n      folder. Please see the :doc:`Loader class <libraries/loader>` for\n      more info.\n   -  Added the ability to assign library objects to your own variable\n      names when you use $this->load->library(). Please see the :doc:`Loader\n      class <libraries/loader>` for more info.\n   -  Added controller class/method info to :doc:`Profiler\n      class <general/profiling>` and support for multiple database\n      connections.\n   -  Improved the \"auto typography\" feature and moved it out of the\n      helper into its own :doc:`Typography\n      Class <libraries/typography>`.\n   -  Improved performance and accuracy of xss_clean(), including\n      reduction of false positives on image/file tests.\n   -  Improved :doc:`Parser class <./libraries/parser>` to allow\n      multiple calls to the parse() function. The output of each is\n      appended in the output.\n   -  Added max_filename option to set a file name length limit in the\n      :doc:`File Upload Class <libraries/file_uploading>`.\n   -  Added set_status_header() function to :doc:`Output\n      class <libraries/output>`.\n   -  Modified :doc:`Pagination <libraries/pagination>` class to only\n      output the \"First\" link when the link for page one would not be\n      shown.\n   -  Added support for mb_strlen in the :doc:`Form\n      Validation <libraries/form_validation>` class so that\n      multi-byte languages will calculate string lengths properly.\n\n-  Database\n\n   -  Improved Active Record class to allow full path column and table\n      names: hostname.database.table.column. Also improved the alias\n      handling.\n   -  Improved how table and column names are escaped and prefixed. It\n      now honors full path names when adding prefixes and escaping.\n   -  Added Active Record caching feature to \"update\" and \"delete\"\n      functions.\n   -  Added removal of non-printing control characters in escape_str()\n      of DB drivers that do not have native PHP escaping mechanisms\n      (mssql, oci8, odbc), to avoid potential SQL errors, and possible\n      sources of SQL injection.\n   -  Added port support to MySQL, MySQLi, and MS SQL database drivers.\n   -  Added driver name variable in each DB driver, based on bug report\n      #4436.\n\n-  Helpers\n\n   -  Added several new \"setting\" functions to the :doc:`Form\n      helper <helpers/form_helper>` that allow POST data to be\n      retrieved and set into forms. These are intended to be used on\n      their own, or with the new :doc:`Form Validation\n      Class <libraries/form_validation>`.\n   -  Added current_url() and uri_segments() to :doc:`URL\n      helper <helpers/url_helper>`.\n   -  Altered auto_link() in the :doc:`URL\n      helper <helpers/url_helper>` so that email addresses with\n      \"+\" included will be linked.\n   -  Added meta() function to :doc:`HTML\n      helper <helpers/html_helper>`.\n   -  Improved accuracy of calculations in :doc:`Number\n      helper <helpers/number_helper>`.\n   -  Removed added newlines (\"\\\\n\") from most form and html helper\n      functions.\n   -  Tightened up validation in the :doc:`Date\n      helper <helpers/date_helper>` function human_to_unix(),\n      and eliminated the POSIX regex.\n   -  Updated :doc:`Date helper <helpers/date_helper>` to match the\n      world's current time zones and offsets.\n   -  Modified url_title() in the :doc:`URL\n      helper <helpers/url_helper>` to remove characters and digits\n      that are part of character entities, to allow dashes, underscores,\n      and periods regardless of the $separator, and to allow uppercase\n      characters.\n   -  Added support for arbitrary attributes in anchor_popup() of the\n      :doc:`URL helper <helpers/url_helper>`.\n\n-  Other Changes\n\n   -  Added :doc:`PHP Style Guide <./general/styleguide>` to docs.\n   -  Added sanitization in xss_clean() for a deprecated HTML tag that\n      could be abused in user input in Internet Explorer.\n   -  Added a few openxml document mime types, and an additional mobile\n      agent to mimes.php and user_agents.php respectively.\n   -  Added a file lock check during caching, before trying to write to\n      the file.\n   -  Modified Cookie key cleaning to unset a few troublesome key names\n      that can be present in certain environments, preventing CI from\n      halting execution.\n   -  Changed the output of the profiler to use style attribute rather\n      than clear, and added the id \"codeigniter_profiler\" to the\n      container div.\n\nBug fixes for 1.7.0\n-------------------\n\n-  Fixed bug in xss_clean() that could remove some desirable tag\n   attributes.\n-  Fixed assorted user guide typos or examples (#4807, #4812, #4840,\n   #4862, #4864, #4899, #4930, #5006, #5071, #5158, #5229, #5254,\n   #5351).\n-  Fixed an edit from 1.6.3 that made the $robots array in\n   user_agents.php go poof.\n-  Fixed a bug in the :doc:`Email library <libraries/email>` with\n   quoted-printable encoding improperly encoding space and tab\n   characters.\n-  Modified XSS sanitization to no longer add semicolons after &[single\n   letter], such as in M&M's, B&B, etc.\n-  Modified XSS sanitization to no longer strip XHTML image tags of\n   closing slashes.\n-  Fixed a bug in the Session class when database sessions are used\n   where upon session update all userdata would be errantly written to\n   the session cookie.\n-  Fixed a bug (#4536) in backups with the MySQL driver where some\n   legacy code was causing certain characters to be double escaped.\n-  Fixed a routing bug (#4661) that occurred when the default route\n   pointed to a subfolder.\n-  Fixed the spelling of \"Dhaka\" in the timezone_menu() function of the\n   :doc:`Date helper. <helpers/date_helper>`\n-  Fixed the spelling of \"raspberry\" in config/smileys.php.\n-  Fixed incorrect parenthesis in form_open() function (#5135).\n-  Fixed a bug that was ignoring case when comparing controller methods\n   (#4560).\n-  Fixed a bug (#4615) that was not setting SMTP authorization settings\n   when using the initialize function.\n-  Fixed a bug in highlight_code() in the :doc:`Text\n   helper <helpers/text_helper>` that would leave a stray </span>\n   in certain cases.\n-  Fixed Oracle bug (#3306) that was preventing multiple queries in one\n   action.\n-  Fixed ODBC bug that was ignoring connection params due to its use of\n   a constructor.\n-  Fixed a DB driver bug with num_rows() that would cause an error with\n   the Oracle driver.\n-  Fixed MS SQL bug (#4915). Added brackets around database name in MS\n   SQL driver when selecting the database, in the event that reserved\n   characters are used in the name.\n-  Fixed a DB caching bug (4718) in which the path was incorrect when no\n   URI segments were present.\n-  Fixed Image_lib class bug #4562. A path was not defined for NetPBM.\n-  Fixed Image_lib class bug #4532. When cropping an image with\n   identical height/width settings on output, a copy is made.\n-  Fixed DB_driver bug (4900), in which a database error was not being\n   logged correctly.\n-  Fixed DB backup bug in which field names were not being escaped.\n-  Fixed a DB Active Record caching bug in which multiple calls to\n   cached data were not being honored.\n-  Fixed a bug in the Session class that was disallowing slashes in the\n   serialized array.\n-  Fixed a Form Validation bug in which the \"isset\" error message was\n   being trigged by the \"required\" rule.\n-  Fixed a spelling error in a Loader error message.\n-  Fixed a bug (5050) with IP validation with empty segments.\n-  Fixed a bug in which the parser was being greedy if multiple\n   identical sets of tags were encountered.\n\nVersion 1.6.3\n=============\n\nRelease Date: June 26, 2008\nHg Tag: v1.6.3\n\nVersion 1.6.3 is a security and maintenance release and is recommended\nfor all users.\n\n-  Database\n\n   -  Modified MySQL/MySQLi Forge class to give explicit names to keys\n   -  Added ability to set multiple column non-primary keys to the\n      :doc:`Forge class <database/forge>`\n   -  Added ability to set additional database config values in :doc:`DSN\n      connections <database/connecting>` via the query string.\n\n-  Libraries\n\n   -  Set the mime type check in the :doc:`Upload\n      class <libraries/file_uploading>` to reference the global\n      mimes variable.\n   -  Added support for query strings to the :doc:`Pagination\n      class <libraries/pagination>`, automatically detected or\n      explicitly declared.\n   -  Added get_post() to the :doc:`Input class <libraries/input>`.\n   -  Documented get() in the :doc:`Input class <libraries/input>`.\n   -  Added the ability to automatically output language items as form\n      labels in the :doc:`Language class <libraries/language>`.\n\n-  Helpers\n\n   -  Added a :doc:`Language helper <helpers/language_helper>`.\n   -  Added a :doc:`Number helper <helpers/number_helper>`.\n   -  :doc:`Form helper <helpers/form_helper>` refactored to allow\n      form_open() and form_fieldset() to accept arrays or strings as\n      arguments.\n\n-  Other changes\n\n   -  Improved security in xss_clean().\n   -  Removed an unused Router reference in _display_cache().\n   -  Added ability to :doc:`use xss_clean() to test\n      images <libraries/input>` for XSS, useful for upload\n      security.\n   -  Considerably expanded list of mobile user-agents in\n      config/user_agents.php.\n   -  Charset information in the userguide has been moved above title\n      for internationalization purposes (#4614).\n   -  Added \"Using Associative Arrays In a Request Parameter\" example to\n      the :doc:`XMLRPC userguide page <libraries/xmlrpc>`.\n   -  Removed maxlength and size as automatically added attributes of\n      form_input() in the :doc:`form helper <helpers/form_helper>`.\n   -  Documented the language file use of byte_format() in the :doc:`number\n      helper <helpers/number_helper>`.\n\nBug fixes for 1.6.3\n-------------------\n\n-  Added a language key for valid_emails in validation_lang.php.\n-  Amended fixes for bug (#3419) with parsing DSN database connections.\n-  Moved the _has_operator() function (#4535) into DB_driver from\n   DB_active_rec.\n-  Fixed a syntax error in upload_lang.php.\n-  Fixed a bug (#4542) with a regular expression in the Image library.\n-  Fixed a bug (#4561) where orhaving() wasn't properly passing values.\n-  Removed some unused variables from the code (#4563).\n-  Fixed a bug where having() was not adding an = into the statement\n   (#4568).\n-  Fixed assorted user guide typos or examples (#4574, #4706).\n-  Added quoted-printable headers to Email class when the multi-part\n   override is used.\n-  Fixed a double opening <p> tag in the index pages of each system\n   directory.\n\nVersion 1.6.2\n=============\n\nRelease Date: May 13, 2008\nHg Tag: 1.6.2\n\n-  Active Record\n\n   -  Added the ability to prevent escaping in having() clauses.\n   -  Added rename_table() into :doc:`DBForge <./database/forge>`.\n   -  Fixed a bug that wasn't allowing escaping to be turned off if the\n      value of a query was NULL.\n   -  DB Forge is now assigned to any models that exist after loading\n      (#3457).\n\n-  Database\n\n   -  Added :doc:`Strict Mode <./database/transactions>` to database\n      transactions.\n   -  Escape behaviour in where() clauses has changed; values in those\n      with the \"FALSE\" argument are no longer escaped (ie: quoted).\n\n-  Config\n\n   -  Added 'application/vnd.ms-powerpoint' to list of mime types.\n   -  Added 'audio/mpg' to list of mime types.\n   -  Added new user-modifiable file constants.php containing file mode\n      and fopen constants.\n   -  Added the ability to set CRLF settings via config in the\n      :doc:`Email <libraries/email>` class.\n\n-  Libraries\n\n   -  Added increased security for filename handling in the Upload\n      library.\n   -  Added increased security for sessions for client-side data\n      tampering.\n   -  The MySQLi forge class is now in sync with MySQL forge.\n   -  Added the ability to set CRLF settings via config in the\n      :doc:`Email <libraries/email>` class.\n   -  :doc:`Unit Testing <libraries/unit_testing>` results are now\n      colour coded, and a change was made to the default template of\n      results.\n   -  Added a valid_emails rule to the Validation class.\n   -  The :doc:`Zip class <libraries/zip>` now exits within download().\n   -  The :doc:`Zip class <libraries/zip>` has undergone a substantial\n      re-write for speed and clarity (thanks stanleyxu for the hard work\n      and code contribution in bug report #3425!)\n\n-  Helpers\n\n   -  Added a Compatibility\n      Helper for using some common\n      PHP 5 functions safely in applications that might run on PHP 4\n      servers (thanks Seppo for the hard work and code contribution!)\n   -  Added form_button() in the :doc:`Form\n      helper <helpers/form_helper>`.\n   -  Changed the radio() and checkbox() functions to default to not\n      checked by default.\n   -  Added the ability to include an optional HTTP Response Code in the\n      redirect() function of the :doc:`URL\n      Helper <helpers/url_helper>`.\n   -  Modified img() in the :doc:`HTML Helper <helpers/html_helper>` to\n      remove an unneeded space (#4208).\n   -  Modified anchor() in the :doc:`URL helper <helpers/url_helper>`\n      to no longer add a default title= attribute (#4209).\n   -  The :doc:`Download helper <helpers/download_helper>` now exits\n      within force_download().\n   -  Added get_dir_file_info(), get_file_info(), and\n      get_mime_by_extension() to the :doc:`File\n      Helper <helpers/file_helper>`.\n   -  Added symbolic_permissions() and octal_permissions() to the\n      :doc:`File helper <helpers/file_helper>`.\n\n-  Plugins\n\n   -  Modified captcha generation to first look for the function\n      imagecreatetruecolor, and fallback to imagecreate if it isn't\n      available (#4226).\n\n-  Other Changes\n\n   -  Added ability for :doc:`xss_clean() <libraries/input>` to accept\n      arrays.\n   -  Removed closing PHP tags from all PHP files to avoid accidental\n      output and potential 'cannot modify headers' errors.\n   -  Removed \"scripts\" from the auto-load search path. Scripts were\n      deprecated in Version 1.4.1 (September 21, 2006). If you still\n      need to use them for legacy reasons, they must now be manually\n      loaded in each Controller.\n   -  Added a :doc:`Reserved Names <general/reserved_names>` page to\n      the userguide, and migrated reserved controller names into it.\n   -  Added a :doc:`Common Functions <general/common_functions>` page\n      to the userguide for globally available functions.\n   -  Improved security and performance of xss_clean().\n\nBugfixes for 1.6.2\n------------------\n\n-  Fixed a bug where SET queries were not being handled as \"write\"\n   queries.\n-  Fixed a bug (#3191) with ORIG_PATH_INFO URI parsing.\n-  Fixed a bug in DB Forge, when inserting an id field (#3456).\n-  Fixed a bug in the table library that could cause identically\n   constructed rows to be dropped (#3459).\n-  Fixed DB Driver and MySQLi result driver checking for resources\n   instead of objects (#3461).\n-  Fixed an AR_caching error where it wasn't tracking table aliases\n   (#3463).\n-  Fixed a bug in AR compiling, where select statements with arguments\n   got incorrectly escaped (#3478).\n-  Fixed an incorrect documentation of $this->load->language (#3520).\n-  Fixed bugs (#3523, #4350) in get_filenames() with recursion and\n   problems with Windows when $include_path is used.\n-  Fixed a bug (#4153) in the XML-RPC class preventing dateTime.iso8601\n   from being used.\n-  Fixed an AR bug with or_where_not_in() (#4171).\n-  Fixed a bug with :doc:`xss_clean() <libraries/input>` that would\n   add semicolons to GET URI variable strings.\n-  Fixed a bug (#4206) in the Directory Helper where the directory\n   resource was not being closed, and minor improvements.\n-  Fixed a bug in the FTP library where delete_dir() was not working\n   recursively (#4215).\n-  Fixed a Validation bug when set_rules() is used with a non-array\n   field name and rule (#4220).\n-  Fixed a bug (#4223) where DB caching would not work for returned DB\n   objects or multiple DB connections.\n-  Fixed a bug in the Upload library that might output the same error\n   twice (#4390).\n-  Fixed an AR bug when joining with a table alias and table prefix\n   (#4400).\n-  Fixed a bug in the DB class testing the $params argument.\n-  Fixed a bug in the Table library where the integer 0 in cell data\n   would be displayed as a blank cell.\n-  Fixed a bug in link_tag() of the :doc:`URL\n   helper <helpers/url_helper>` where a key was passed instead of\n   a value.\n-  Fixed a bug in DB_result::row() that prevented it from returning\n   individual fields with MySQL NULL values.\n-  Fixed a bug where SMTP emails were not having dot transformation\n   performed on lines that begin with a dot.\n-  Fixed a bug in display_error() in the DB driver that was\n   instantiating new Language and Exception objects, and not using the\n   error heading.\n-  Fixed a bug (#4413) where a URI containing slashes only e.g.\n   '\\http://example.com/index.php?//' would result in PHP errors\n-  Fixed an array to string conversion error in the Validation library\n   (#4425)\n-  Fixed bug (#4451, #4299, #4339) where failed transactions will not\n   rollback when debug mode is enabled.\n-  Fixed a bug (#4506) with overlay_watermark() in the Image library\n   preventing support for PNG-24s with alpha transparency\n-  Fixed assorted user guide typos (#3453, #4364, #4379, #4399, #4408,\n   #4412, #4448, #4488).\n\nVersion 1.6.1\n=============\n\nRelease Date: February 12, 2008\nHg Tag: 1.6.1\n\n-  Active Record\n\n   -  Added :ref:`Active Record\n      Caching <ar-caching>`.\n   -  Made Active Record fully database-prefix aware.\n\n-  Database drivers\n\n   -  Added support for setting client character set and collation for\n      MySQLi.\n\n-  Core Changes\n\n   -  Modified xss_clean() to be more intelligent with its handling of\n      URL encoded strings.\n   -  Added $_SERVER, $_FILES, $_ENV, and $_SESSION to sanitization\n      of globals.\n   -  Added a :doc:`Path Helper <./helpers/path_helper>`.\n   -  Simplified _reindex_segments() in the URI class.\n   -  Escaped the '-' in the default 'permitted_uri_chars' config\n      item, to prevent errors if developers just try to add additional\n      characters to the end of the default expression.\n   -  Modified method calling to controllers to show a 404 when a\n      private or protected method is accessed via a URL.\n   -  Modified framework initiated 404s to log the controller and method\n      for invalid requests.\n\n-  Helpers\n\n   -  Modified get_filenames() in the File Helper to return FALSE if\n      the $source_dir is not readable.\n\nBugfixes for 1.6.1\n------------------\n\n-  Deprecated is_numeric as a validation rule. Use of numeric and\n   integer are preferred.\n-  Fixed bug (#3379) in DBForge with SQLite for table creation.\n-  Made Active Record fully database prefix aware (#3384).\n-  Fixed a bug where DBForge was outputting invalid SQL in Postgres by\n   adding brackets around the tables in FROM.\n-  Changed the behaviour of Active Record's update() to make the WHERE\n   clause optional (#3395).\n-  Fixed a bug (#3396) where certain POST variables would cause a PHP\n   warning.\n-  Fixed a bug in query binding (#3402).\n-  Changed order of SQL keywords in the Profiler $highlight array so OR\n   would not be highlighted before ORDER BY.\n-  Fixed a bug (#3404) where the MySQLi driver was testing if\n   $this->conn_id was a resource instead of an object.\n-  Fixed a bug (#3419) connecting to a database via a DSN string.\n-  Fixed a bug (#3445) where the routed segment array was not re-indexed\n   to begin with 1 when the default controller is used.\n-  Fixed assorted user guide typos.\n\nVersion 1.6.0\n=============\n\nRelease Date: January 30, 2008\n\n-  DBForge\n\n   -  Added :doc:`DBForge <./database/forge>` to the database tools.\n   -  Moved create_database() and drop_database() into\n      :doc:`DBForge <./database/forge>`.\n   -  Added add_field(), add_key(), create_table(), drop_table(),\n      add_column(), drop_column(), modify_column() into\n      :doc:`DBForge <./database/forge>`.\n\n-  Active Record\n\n   -  Added protect_identifiers() in :doc:`Active\n      Record <./database/query_builder>`.\n   -  All AR queries are backticked if appropriate to the database.\n   -  Added where_in(), or_where_in(), where_not_in(),\n      or_where_not_in(), not_like() and or_not_like() to :doc:`Active\n      Record <./database/query_builder>`.\n   -  Added support for limit() into update() and delete() statements in\n      :doc:`Active Record <./database/query_builder>`.\n   -  Added empty_table() and truncate_table() to :doc:`Active\n      Record <./database/query_builder>`.\n   -  Added the ability to pass an array of tables to the delete()\n      statement in :doc:`Active Record <./database/query_builder>`.\n   -  Added count_all_results() function to :doc:`Active\n      Record <./database/query_builder>`.\n   -  Added select_max(), select_min(), select_avg() and\n      select_sum() to :doc:`Active Record <./database/query_builder>`.\n   -  Added the ability to use aliases with joins in :doc:`Active\n      Record <./database/query_builder>`.\n   -  Added a third parameter to Active Record's like() clause to\n      control where the wildcard goes.\n   -  Added a third parameter to set() in :doc:`Active\n      Record <./database/query_builder>` that withholds escaping\n      data.\n   -  Changed the behaviour of variables submitted to the where() clause\n      with no values to auto set \"IS NULL\"\n\n-  Other Database Related\n\n   -  MySQL driver now requires MySQL 4.1+\n   -  Added $this->DB->save_queries variable to DB driver, enabling\n      queries to get saved or not. Previously they were always saved.\n   -  Added $this->db->dbprefix() to manually add database prefixes.\n   -  Added 'random' as an order_by() option , and removed \"rand()\" as\n      a listed option as it was MySQL only.\n   -  Added a check for NULL fields in the MySQL database backup\n      utility.\n   -  Added \"constrain_by_prefix\" parameter to db->list_table()\n      function. If set to TRUE it will limit the result to only table\n      names with the current prefix.\n   -  Deprecated from Active Record; getwhere() for get_where();\n      groupby() for group_by(); havingor() for having_or(); orderby()\n      for order_by; orwhere() for or_where(); and orlike() for\n      or_like().\n   -  Modified csv_from_result() to output CSV data more in the spirit\n      of basic rules of RFC 4180.\n   -  Added 'char_set' and 'dbcollat' database configuration settings,\n      to explicitly set the client communication properly.\n   -  Removed 'active_r' configuration setting and replaced with a\n      global $active_record setting, which is more in harmony with the\n      global nature of the behavior (#1834).\n\n-  Core changes\n\n   -  Added ability to load multiple views, whose content will be\n      appended to the output in the order loaded.\n   -  Added the ability to :doc:`auto-load <./general/autoloader>`\n      :doc:`Models <./general/models>`.\n   -  Reorganized the URI and Routes classes for better clarity.\n   -  Added Compat.php to allow function overrides for older versions of\n      PHP or PHP environments missing certain extensions / libraries\n   -  Added memory usage, GET, URI string data, and individual query\n      execution time to Profiler output.\n   -  Deprecated Scaffolding.\n   -  Added is_really_writable() to Common.php to provide a\n      cross-platform reliable method of testing file/folder writability.\n\n-  Libraries\n\n   -  Changed the load protocol of Models to allow for extension.\n   -  Strengthened the Encryption library to help protect against man in\n      the middle attacks when MCRYPT_MODE_CBC mode is used.\n   -  Added Flashdata variables, session_id regeneration and\n      configurable session update times to the :doc:`Session\n      class. <./libraries/sessions>`\n   -  Removed 'last_visit' from the Session class.\n   -  Added a language entry for valid_ip validation error.\n   -  Modified ``prep_for_form()`` in the Validation class to accept\n      arrays, adding support for POST array validation (via callbacks\n      only)\n   -  Added an \"integer\" rule into the Validation library.\n   -  Added valid_base64() to the Validation library.\n   -  Documented clear() in the :doc:`Image\n      Processing <./libraries/image_lib>` library.\n   -  Changed the behaviour of custom callbacks so that they no longer\n      trigger the \"required\" rule.\n   -  Modified Upload class $_FILES error messages to be more precise.\n   -  Moved the safe mode and auth checks for the Email library into the\n      constructor.\n   -  Modified variable names in _ci_load() method of Loader class to\n      avoid conflicts with view variables.\n   -  Added a few additional mime type variations for CSV.\n   -  Enabled the 'system' methods for the XML-RPC Server library,\n      except for 'system.multicall' which is still disabled.\n\n-  Helpers & Plugins\n\n   -  Added link_tag() to the :doc:`HTML\n      helper. <./helpers/html_helper>`\n   -  Added img() to the :doc:`HTML helper. <./helpers/html_helper>`\n   -  Added ability to :doc:`\"extend\" Helpers <./general/helpers>`.\n   -  Added an *Email Helper* into core helpers.\n   -  Added strip_quotes() function to :doc:`string\n      helper <./helpers/string_helper>`.\n   -  Added reduce_multiples() function to :doc:`string\n      helper <./helpers/string_helper>`.\n   -  Added quotes_to_entities() function to :doc:`string\n      helper <./helpers/string_helper>`.\n   -  Added form_fieldset(), form_fieldset_close(), form_label(),\n      and form_reset() function to :doc:`form\n      helper <./helpers/form_helper>`.\n   -  Added support for external urls in form_open().\n   -  Removed support for db_backup in MySQLi due to incompatible\n      functions.\n   -  Javascript Calendar plugin now uses the months and days from the\n      calendar language file, instead of hard-coded values,\n      internationalizing it.\n\n-  Documentation Changes\n\n   -  Added Writing Documentation section\n      for the community to use in writing their own documentation.\n   -  Added titles to all user manual pages.\n   -  Added attributes into <html> of userguide for valid html.\n   -  Added :doc:`Zip Encoding Class <libraries/zip>`\n      to the table of contents of the userguide.\n   -  Moved part of the userguide menu javascript to an external file.\n   -  Documented distinct() in :doc:`Active\n      Record <./database/query_builder>`.\n   -  Documented the timezones() function in the :doc:`Date\n      Helper <./helpers/date_helper>`.\n   -  Documented unset_userdata in the :doc:`Session\n      class <./libraries/sessions>`.\n   -  Documented 2 config options to the :doc:`Database\n      configuration <./database/configuration>` page.\n\nBug fixes for Version 1.6.0\n---------------------------\n\n-  Fixed a bug (#1813) preventing using $CI->db in the same application\n   with returned database objects.\n-  Fixed a bug (#1842) where the $this->uri->rsegments array would not\n   include the 'index' method if routed to the controller without an\n   implicit method.\n-  Fixed a bug (#1872) where word_limiter() was not retaining\n   whitespace.\n-  Fixed a bug (#1890) in csv_from_result() where content that\n   included the delimiter would break the file.\n-  Fixed a bug (#2542)in the clean_email() method of the Email class to\n   allow for non-numeric / non-sequential array keys.\n-  Fixed a bug (#2545) in _html_entity_decode_callback() when\n   'global_xss_filtering' is enabled.\n-  Fixed a bug (#2668) in the :doc:`parser class <./libraries/parser>`\n   where numeric data was ignored.\n-  Fixed a bug (#2679) where the \"previous\" pagination link would get\n   drawn on the first page.\n-  Fixed a bug (#2702) in _object_to_array that broke some types of\n   inserts and updates.\n-  Fixed a bug (#2732) in the SQLite driver for PHP 4.\n-  Fixed a bug (#2754) in Pagination to scan for non-positive\n   num_links.\n-  Fixed a bug (#2762) in the :doc:`Session\n   library <./libraries/sessions>` where user agent matching would\n   fail on user agents ending with a space.\n-  Fixed a bug (#2784) $field_names[] vs $Ffield_names[] in postgres\n   and sqlite drivers.\n-  Fixed a bug (#2810) in the typography helper causing extraneous\n   paragraph tags when string contains tags.\n-  Fixed a bug (#2849) where arguments passed to a subfolder controller\n   method would be incorrectly shifted, dropping the 3rd segment value.\n-  Fixed a bug (#2858) which referenced a wrong variable in the Image\n   class.\n-  Fixed a bug (#2875)when loading plugin files as _plugin. and not\n   _pi.\n-  Fixed a bug (#2912) in get_filenames() in the :doc:`File\n   Helper <helpers/file_helper>` where the array wasn't cleared\n   after each call.\n-  Fixed a bug (#2974) in highlight_phrase() that caused an error with\n   slashes.\n-  Fixed a bug (#3003) in the Encryption Library to support modes other\n   than MCRYPT_MODE_ECB\n-  Fixed a bug (#3015) in the :doc:`User Agent\n   library <./libraries/user_agent>` where more than 2 languages\n   where not reported with languages().\n-  Fixed a bug (#3017) in the :doc:`Email <./libraries/email>` library\n   where some timezones were calculated incorrectly.\n-  Fixed a bug (#3024) in which master_dim wasn't getting reset by\n   clear() in the Image library.\n-  Fixed a bug (#3156) in Text Helper highlight_code() causing PHP tags\n   to be handled incorrectly.\n-  Fixed a bug (#3166) that prevented num_rows from working in Oracle.\n-  Fixed a bug (#3175) preventing certain libraries from working\n   properly when autoloaded in PHP 4.\n-  Fixed a bug (#3267) in the Typography Helper where unordered list was\n   listed \"un.\n-  Fixed a bug (#3268) where the Router could leave '/' as the path.\n-  Fixed a bug (#3279) where the Email class was sending the wrong\n   Content-Transfer-Encoding for some character sets.\n-  Fixed a bug (#3284) where the rsegment array would not be set\n   properly if the requested URI contained more segments than the routed\n   URI.\n-  Removed extraneous load of $CFG in _display_cache() of the Output\n   class (#3285).\n-  Removed an extraneous call to loading models (#3286).\n-  Fixed a bug (#3310) with sanitization of globals in the Input class\n   that could unset CI's global variables.\n-  Fixed a bug (#3314) which would cause the top level path to be\n   deleted in delete_files() of the File helper.\n-  Fixed a bug (#3328) where the smiley helper might return an undefined\n   variable.\n-  Fixed a bug (#3330) in the FTP class where a comparison wasn't\n   getting made.\n-  Removed an unused parameter from Profiler (#3332).\n-  Fixed a bug in database driver where num_rows property wasn't\n   getting updated.\n-  Fixed a bug in the :doc:`upload\n   library <./libraries/file_uploading>` when allowed_files\n   wasn't defined.\n-  Fixed a bug in word_wrap() of the Text Helper that incorrectly\n   referenced an object.\n-  Fixed a bug in Validation where valid_ip() wasn't called properly.\n-  Fixed a bug in Validation where individual error messages for\n   checkboxes wasn't supported.\n-  Fixed a bug in captcha calling an invalid PHP function.\n-  Fixed a bug in the cookie helper \"set_cookie\" function. It was not\n   honoring the config settings.\n-  Fixed a bug that was making validation callbacks required even when\n   not set as such.\n-  Fixed a bug in the XML-RPC library so if a type is specified, a more\n   intelligent decision is made as to the default type.\n-  Fixed an example of comma-separated emails in the email library\n   documentation.\n-  Fixed an example in the Calendar library for Showing Next/Previous\n   Month Links.\n-  Fixed a typo in the database language file.\n-  Fixed a typo in the image language file \"suppor\" to \"support\".\n-  Fixed an example for XML RPC.\n-  Fixed an example of accept_charset() in the :doc:`User Agent\n   Library <./libraries/user_agent>`.\n-  Fixed a typo in the docblock comments that had CodeIgniter spelled\n   CodeIgnitor.\n-  Fixed a typo in the :doc:`String Helper <./helpers/string_helper>`\n   (uniquid changed to uniqid).\n-  Fixed typos in the email Language class\n   (email_attachment_unredable, email_filed_smtp_login), and FTP\n   Class (ftp_unable_to_remame).\n-  Added a stripslashes() into the Upload Library.\n-  Fixed a series of grammatical and spelling errors in the language\n   files.\n-  Fixed assorted user guide typos.\n\nVersion 1.5.4\n=============\n\nRelease Date: July 12, 2007\n\n-  Added :doc:`custom Language files <./libraries/language>` to the\n   :doc:`autoload <./general/autoloader>` options.\n-  Added stripslashes() to the _clean_input_data() function in the\n   :doc:`Input class <./libraries/input>` when magic quotes is on so\n   that data will always be un-slashed within the framework.\n-  Added array to string into the :doc:`profiler <general/profiling>`.\n-  Added some additional mime types in application/config/mimes.php.\n-  Added filename_security() method to :doc:`Input\n   library <./libraries/input>`.\n-  Added some additional arguments to the :doc:`Inflection\n   helper <./helpers/inflector_helper>` singular() to compensate\n   for words ending in \"s\". Also added a force parameter to pluralize().\n-  Added $config['charset'] to the config file. Default value is\n   'UTF-8', used in some string handling functions.\n-  Fixed MSSQL insert_id().\n-  Fixed a logic error in the DB trans_status() function. It was\n   incorrectly returning TRUE on failure and FALSE on success.\n-  Fixed a bug that was allowing multiple load attempts on extended\n   classes.\n-  Fixed a bug in the bootstrap file that was incorrectly attempting to\n   discern the full server path even when it was explicity set by the\n   user.\n-  Fixed a bug in the escape_str() function in the MySQL driver.\n-  Fixed a typo in the :doc:`Calendar library <./libraries/calendar>`\n-  Fixed a typo in rpcs.php library\n-  Fixed a bug in the :doc:`Zip library <./libraries/zip>`, providing\n   PC Zip file compatibility with Mac OS X\n-  Fixed a bug in router that was ignoring the scaffolding route for\n   optimization\n-  Fixed an IP validation bug.\n-  Fixed a bug in display of POST keys in the\n   :doc:`Profiler <./general/profiling>` output\n-  Fixed a bug in display of queries with characters that would be\n   interpreted as HTML in the :doc:`Profiler <./general/profiling>`\n   output\n-  Fixed a bug in display of Email class print debugger with characters\n   that would be interpreted as HTML in the debugging output\n-  Fixed a bug in the Content-Transfer-Encoding of HTML emails with the\n   quoted-printable MIME type\n-  Fixed a bug where one could unset certain PHP superglobals by setting\n   them via GET or POST data\n-  Fixed an undefined function error in the insert_id() function of the\n   PostgreSQL driver\n-  Fixed various doc typos.\n-  Documented two functions from the :doc:`String\n   helper <./helpers/string_helper>` that were missing from the\n   user guide: ``trim_slashes()`` and ``reduce_double_slashes()``.\n-  Docs now validate to XHTML 1 transitional\n-  Updated the XSS Filtering to take into account the IE expression()\n   ability and improved certain deletions to prevent possible exploits\n-  Modified the Router so that when Query Strings are Enabled, the\n   controller trigger and function trigger values are sanitized for\n   filename include security.\n-  Modified the is_image() method in the Upload library to take into\n   account Windows IE 6/7 eccentricities when dealing with MIMEs\n-  Modified XSS Cleaning routine to be more performance friendly and\n   compatible with PHP 5.2's new PCRE backtrack and recursion limits.\n-  Modified the :doc:`URL Helper <./helpers/url_helper>` to type cast\n   the $title as a string in case a numeric value is supplied\n-  Modified Form Helper form_dropdown() to type cast the keys and\n   values of the options array as strings, allowing numeric values to be\n   properly set as 'selected'\n-  Deprecated the use if is_numeric() in various places since it allows\n   periods. Due to compatibility problems with ctype_digit(), making it\n   unreliable in some installations, the following regular expression\n   was used instead: preg_match(\"/[^0-9]/\", $n)\n-  Deprecated: APPVER has been deprecated and replaced with CI_VERSION\n   for clarity.\n\nVersion 1.5.3\n=============\n\nRelease Date: April 15, 2007\n\n-  Added array to string into the profiler\n-  Code Igniter references updated to CodeIgniter\n-  pMachine references updated to EllisLab\n-  Fixed a bug in the ``repeater()`` function of :doc:`string\n   helper <./helpers/string_helper>`.\n-  Fixed a bug in ODBC driver\n-  Fixed a bug in result_array() that was returning an empty array when\n   no result is produced.\n-  Fixed a bug in the redirect function of the :doc:`url\n   helper <./helpers/url_helper>`.\n-  Fixed an undefined variable in Loader\n-  Fixed a version bug in the Postgres driver\n-  Fixed a bug in the textarea function of the form helper for use with\n   strings\n-  Fixed doc typos.\n\nVersion 1.5.2\n=============\n\nRelease Date: February 13, 2007\n\n-  Added subversion information\n   to the :doc:`downloads <installation/downloads>` page.\n-  Added support for captions in the :doc:`Table\n   Library <./libraries/table>`\n-  Fixed a bug in the\n   :doc:`download_helper <helpers/download_helper>` that was causing\n   Internet Explorer to load rather than download\n-  Fixed a bug in the Active Record Join function that was not taking\n   table prefixes into consideration.\n-  Removed unescaped variables in error messages of Input and Router\n   classes\n-  Fixed a bug in the Loader that was causing errors on Libraries loaded\n   twice. A debug message is now silently made in the log.\n-  Fixed a bug in the :doc:`form helper <helpers/form_helper>` that\n   gave textarea a value attribute\n-  Fixed a bug in the :doc:`Image Library <libraries/image_lib>` that\n   was ignoring resizing the same size image\n-  Fixed some doc typos.\n\nVersion 1.5.1\n=============\n\nRelease Date: November 23, 2006\n\n-  Added support for submitting arrays of libraries in the\n   $this->load->library function.\n-  Added support for naming custom library files in lower or uppercase.\n-  Fixed a bug related to output buffering.\n-  Fixed a bug in the active record class that was not resetting query\n   data after a completed query.\n-  Fixed a bug that was suppressing errors in controllers.\n-  Fixed a problem that can cause a loop to occur when the config file\n   is missing.\n-  Fixed a bug that occurred when multiple models were loaded with the\n   third parameter set to TRUE.\n-  Fixed an oversight that was not unsetting globals properly in the\n   input sanitize function.\n-  Fixed some bugs in the Oracle DB driver.\n-  Fixed an incorrectly named variable in the MySQLi result driver.\n-  Fixed some doc typos.\n\nVersion 1.5.0.1\n===============\n\nRelease Date: October 31, 2006\n\n-  Fixed a problem in which duplicate attempts to load helpers and\n   classes were not being stopped.\n-  Fixed a bug in the word_wrap() helper function.\n-  Fixed an invalid color Hex number in the Profiler class.\n-  Fixed a corrupted image in the user guide.\n\nVersion 1.5.0\n=============\n\nRelease Date: October 30, 2006\n\n-  Added :doc:`DB utility class <./database/utilities>`, permitting DB\n   backups, CVS or XML files from DB results, and various other\n   functions.\n-  Added :doc:`Database Caching Class <./database/caching>`.\n-  Added :doc:`transaction support <./database/transactions>` to the\n   database classes.\n-  Added :doc:`Profiler Class <./general/profiling>` which generates a\n   report of Benchmark execution times, queries, and POST data at the\n   bottom of your pages.\n-  Added :doc:`User Agent Library <./libraries/user_agent>` which\n   allows browsers, robots, and mobile devises to be identified.\n-  Added :doc:`HTML Table Class <./libraries/table>` , enabling tables\n   to be generated from arrays or database results.\n-  Added :doc:`Zip Encoding Library <./libraries/zip>`.\n-  Added :doc:`FTP Library <./libraries/ftp>`.\n-  Added the ability to :doc:`extend\n   libraries <./general/creating_libraries>` and :doc:`extend core\n   classes <./general/core_classes>`, in addition to being able to\n   replace them.\n-  Added support for storing :doc:`models within\n   sub-folders <./general/models>`.\n-  Added :doc:`Download Helper <./helpers/download_helper>`.\n-  Added :doc:`simple_query() <./database/queries>` function to the\n   database classes\n-  Added ``standard_date()`` function function to the :doc:`Date Helper <helpers/date_helper>`.\n-  Added :doc:`$query->free_result() <./database/results>` to database\n   class.\n-  Added :doc:`$query->list_fields() <./database/metadata>` function to\n   database class\n-  Added :doc:`$this->db->platform() <./database/helpers>` function\n-  Added new :doc:`File Helper <./helpers/file_helper>`:\n   get_filenames()\n-  Added new helper: *Smiley Helper*\n-  Added support for <ul> and <ol> lists in the :doc:`HTML\n   Helper <./helpers/html_helper>`\n-  Added the ability to rewrite :doc:`short\n   tags <./general/alternative_php>` on-the-fly, converting them\n   to standard PHP statements, for those servers that do not support\n   short tags. This allows the cleaner syntax to be used regardless of\n   whether it's supported by the server.\n-  Added the ability to :doc:`rename or relocate the \"application\"\n   folder <./general/managing_apps>`.\n-  Added more thorough initialization in the upload class so that all\n   class variables are reset.\n-  Added \"is_numeric\" to validation, which uses the native PHP\n   is_numeric function.\n-  Improved the URI handler to make it more reliable when the\n   $config['uri_protocol'] item is set to AUTO.\n-  Moved most of the functions in the Controller class into the Loader\n   class, allowing fewer reserved function names for controllers when\n   running under PHP 5.\n-  Updated the DB Result class to return an empty array when\n   $query->result() doesn't produce a result.\n-  Updated the input->cookie() and input->post() functions in :doc:`Input\n   Class <./libraries/input>` to permit arrays contained cookies\n   that are arrays to be run through the XSS filter.\n-  Documented three functions from the Validation\n   class that were missing from the user\n   guide: set_select(), set_radio(), and set_checkbox().\n-  Fixed a bug in the Email class related to SMTP Helo data.\n-  Fixed a bug in the word wrapping helper and function in the email\n   class.\n-  Fixed a bug in the validation class.\n-  Fixed a bug in the typography helper that was incorrectly wrapping\n   block level elements in paragraph tags.\n-  Fixed a problem in the ``form_prep()`` function that was double encoding\n   entities.\n-  Fixed a bug that affects some versions of PHP when output buffering\n   is nested.\n-  Fixed a bug that caused CI to stop working when the PHP magic\n   __get() or __set() functions were used within models or\n   controllers.\n-  Fixed a pagination bug that was permitting negative values in the\n   URL.\n-  Fixed an oversight in which the Loader class was not allowed to be\n   extended.\n-  Changed _get_config() to get_config() since the function is not a\n   private one.\n-  **Deprecated \"init\" folder**. Initialization happens automatically\n   now. :doc:`Please see documentation <./general/creating_libraries>`.\n-  **Deprecated** $this->db->field_names() USE\n   $this->db->list_fields()\n-  **Deprecated** the $config['log_errors'] item from the config.php\n   file. Instead, $config['log_threshold'] can be set to \"0\" to turn it\n   off.\n\nVersion 1.4.1\n=============\n\nRelease Date: September 21, 2006\n\n-  Added a new feature that passes URI segments directly to your\n   function calls as parameters. See the\n   :doc:`Controllers <general/controllers>` page for more info.\n-  Added support for a function named _output(), which when used in\n   your controllers will received the final rendered output from the\n   output class. More info in the :doc:`Controllers <general/controllers>`\n   page.\n-  Added several new functions in the :doc:`URI\n   Class <./libraries/uri>` to let you retrieve and manipulate URI\n   segments that have been re-routed using the :doc:`URI\n   Routing <general/routing>` feature. Previously, the URI class did not\n   permit you to access any re-routed URI segments, but now it does.\n-  Added :doc:`$this->output->set_header() <./libraries/output>`\n   function, which allows you to set server headers.\n-  Updated plugins, helpers, and language classes to allow your\n   application folder to contain its own plugins, helpers, and language\n   folders. Previously they were always treated as global for your\n   entire installation. If your application folder contains any of these\n   resources they will be used *instead* the global ones.\n-  Added :doc:`Inflector helper <./helpers/inflector_helper>`.\n-  Added element() function in the :doc:`array\n   helper <./helpers/array_helper>`.\n-  Added RAND() to active record orderby() function.\n-  Added delete_cookie() and get_cookie() to :doc:`Cookie\n   helper <./helpers/cookie_helper>`, even though the input class\n   has a cookie fetching function.\n-  Added Oracle database driver (still undergoing testing so it might\n   have some bugs).\n-  Added the ability to combine pseudo-variables and php variables in\n   the template parser class.\n-  Added output compression option to the config file.\n-  Removed the is_numeric test from the db->escape() function.\n-  Fixed a MySQLi bug that was causing error messages not to contain\n   proper error data.\n-  Fixed a bug in the email class which was causing it to ignore\n   explicitly set alternative headers.\n-  Fixed a bug that was causing a PHP error when the Exceptions class\n   was called within the get_config() function since it was causing\n   problems.\n-  Fixed an oversight in the cookie helper in which the config file\n   cookie settings were not being honored.\n-  Fixed an oversight in the upload class. An item mentioned in the 1.4\n   changelog was missing.\n-  Added some code to allow email attachments to be reset when sending\n   batches of email.\n-  Deprecated the application/scripts folder. It will continue to work\n   for legacy users, but it is recommended that you create your own\n   :doc:`libraries <./general/libraries>` or\n   :doc:`models <./general/models>` instead. It was originally added\n   before CI had user libraries or models, but it's not needed anymore.\n-  Deprecated the $autoload['core'] item from the autoload.php file.\n   Instead, please now use: $autoload['libraries']\n-  Deprecated the following database functions:\n   $this->db->smart_escape_str() and $this->db->fields().\n\nVersion 1.4.0\n=============\n\nRelease Date: September 17, 2006\n\n-  Added :doc:`Hooks <./general/hooks>` feature, enabling you to tap\n   into and modify the inner workings of the framework without hacking\n   the core files.\n-  Added the ability to organize controller files :doc:`into\n   sub-folders <general/controllers>`. Kudos to Marco for\n   suggesting this (and the next two) feature.\n-  Added regular expressions support for :doc:`routing\n   rules <./general/routing>`.\n-  Added the ability to :doc:`remap function\n   calls <./general/controllers>` within your controllers.\n-  Added the ability to :doc:`replace core system\n   classes <./general/core_classes>` with your own classes.\n-  Added support for % character in URL.\n-  Added the ability to supply full URLs using the\n   :doc:`anchor() <./helpers/url_helper>` helper function.\n-  Added mode parameter to :doc:`file_write() <./helpers/file_helper>`\n   helper.\n-  Added support for changing the port number in the :doc:`Postgres\n   driver <./database/configuration>`.\n-  Moved the list of \"allowed URI characters\" out of the Router class\n   and into the config file.\n-  Moved the MIME type array out of the Upload class and into its own\n   file in the application/config/ folder.\n-  Updated the Upload class to allow the upload field name to be set\n   when calling :doc:`do_upload() <./libraries/file_uploading>`.\n-  Updated the :doc:`Config Library <./libraries/config>` to be able to\n   load config files silently, and to be able to assign config files to\n   their own index (to avoid collisions if you use multiple config\n   files).\n-  Updated the URI Protocol code to allow more options so that URLs will\n   work more reliably in different environments.\n-  Updated the form_open() helper to allow the GET method to be used.\n-  Updated the MySQLi execute() function with some code to help prevent\n   lost connection errors.\n-  Updated the SQLite Driver to check for object support before\n   attempting to return results as objects. If unsupported it returns an\n   array.\n-  Updated the Models loader function to allow multiple loads of the\n   same model.\n-  Updated the MS SQL driver so that single quotes are escaped.\n-  Updated the Postgres and ODBC drivers for better compatibility.\n-  Removed a strtolower() call that was changing URL segments to lower\n   case.\n-  Removed some references that were interfering with PHP 4.4.1\n   compatibility.\n-  Removed backticks from Postgres class since these are not needed.\n-  Renamed display() to _display() in the Output class to make it clear\n   that it's a private function.\n-  Deprecated the hash() function due to a naming conflict with a native\n   PHP function with the same name. Please use dohash() instead.\n-  Fixed an bug that was preventing the input class from unsetting GET\n   variables.\n-  Fixed a router bug that was making it too greedy when matching end\n   segments.\n-  Fixed a bug that was preventing multiple discrete database calls.\n-  Fixed a bug in which loading a language file was producing a \"file\n   contains no data\" message.\n-  Fixed a session bug caused by the XSS Filtering feature inadvertently\n   changing the case of certain words.\n-  Fixed some missing prefixes when using the database prefix feature.\n-  Fixed a typo in the Calendar class (cal_november).\n-  Fixed a bug in the form_checkbox() helper.\n-  Fixed a bug that was allowing the second segment of the URI to be\n   identical to the class name.\n-  Fixed an evaluation bug in the database initialization function.\n-  Fixed a minor bug in one of the error messages in the language class.\n-  Fixed a bug in the date helper timespan function.\n-  Fixed an undefined variable in the DB Driver class.\n-  Fixed a bug in which dollar signs used as binding replacement values\n   in the DB class would be treated as RegEx back-references.\n-  Fixed a bug in the set_hash() function which was preventing MD5 from\n   being used.\n-  Fixed a couple bugs in the Unit Testing class.\n-  Fixed an incorrectly named variable in the Validation class.\n-  Fixed an incorrectly named variable in the URI class.\n-  Fixed a bug in the config class that was preventing the base URL from\n   being called properly.\n-  Fixed a bug in the validation class that was not permitting callbacks\n   if the form field was empty.\n-  Fixed a problem that was preventing scaffolding from working properly\n   with MySQLi.\n-  Fixed some MS SQL bugs.\n-  Fixed some doc typos.\n\nVersion 1.3.3\n=============\n\nRelease Date: June 1, 2006\n\n-  Models do **not** connect automatically to the database as of this\n   version. :doc:`More info here <./general/models>`.\n-  Updated the Sessions class to utilize the active record class when\n   running session related queries. Previously the queries assumed MySQL\n   syntax.\n-  Updated alternator() function to re-initialize when called with no\n   arguments, allowing multiple calls.\n-  Fixed a bug in the active record \"having\" function.\n-  Fixed a problem in the validation class which was making checkboxes\n   be ignored when required.\n-  Fixed a bug in the word_limiter() helper function. It was cutting\n   off the fist word.\n-  Fixed a bug in the xss_clean function due to a PHP bug that affects\n   some versions of html_entity_decode.\n-  Fixed a validation bug that was preventing rules from being set twice\n   in one controller.\n-  Fixed a calendar bug that was not letting it use dynamically loaded\n   languages.\n-  Fixed a bug in the active record class when using WHERE clauses with\n   LIKE\n-  Fixed a bug in the hash() security helper.\n-  Fixed some typos.\n\nVersion 1.3.2\n=============\n\nRelease Date: April 17, 2006\n\n-  Changed the behavior of the validation class such that if a\n   \"required\" rule is NOT explicitly stated for a field then all other\n   tests get ignored.\n-  Fixed a bug in the Controller class that was causing it to look in\n   the local \"init\" folder instead of the main system one.\n-  Fixed a bug in the init_pagination file. The $config item was not\n   being set correctly.\n-  Fixed a bug in the auto typography helper that was causing\n   inconsistent behavior.\n-  Fixed a couple bugs in the Model class.\n-  Fixed some documentation typos and errata.\n\nVersion 1.3.1\n=============\n\nRelease Date: April 11, 2006\n\n-  Added a :doc:`Unit Testing Library <./libraries/unit_testing>`.\n-  Added the ability to pass objects to the **insert()** and\n   **update()** database functions. This feature enables you to (among\n   other things) use your :doc:`Model class <./general/models>`\n   variables to run queries with. See the Models page for details.\n-  Added the ability to pass objects to the :doc:`view loading\n   function <./general/views>`: $this->load->view('my_view',\n   $object);\n-  Added getwhere function to :doc:`Active Record\n   class <./database/query_builder>`.\n-  Added count_all function to :doc:`Active Record\n   class <./database/query_builder>`.\n-  Added language file for scaffolding and fixed a scaffolding bug that\n   occurs when there are no rows in the specified table.\n-  Added :doc:`$this->db->last_query() <./database/queries>`, which\n   allows you to view your last query that was run.\n-  Added a new mime type to the upload class for better compatibility.\n-  Changed how cache files are read to prevent PHP errors if the cache\n   file contains an XML tag, which PHP wants to interpret as a short\n   tag.\n-  Fixed a bug in a couple of the active record functions (where and\n   orderby).\n-  Fixed a bug in the image library when realpath() returns false.\n-  Fixed a bug in the Models that was preventing libraries from being\n   used within them.\n-  Fixed a bug in the \"exact_length\" function of the validation class.\n-  Fixed some typos in the user guide\n\nVersion 1.3\n===========\n\nRelease Date: April 3, 2006\n\n-  Added support for :doc:`Models <general/models>`.\n-  Redesigned the database libraries to support additional RDBMs\n   (Postgres, MySQLi, etc.).\n-  Redesigned the :doc:`Active Record class <./database/query_builder>`\n   to enable more varied types of queries with simpler syntax, and\n   advanced features like JOINs.\n-  Added a feature to the database class that lets you run :doc:`custom\n   function calls <./database/call_function>`.\n-  Added support for :doc:`private functions <general/controllers>` in your\n   controllers. Any controller function name that starts with an\n   underscore will not be served by a URI request.\n-  Added the ability to pass your own initialization parameters to your\n   :doc:`custom core libraries <general/creating_libraries>` when using\n   $this->load->library()\n-  Added support for running standard :doc:`query string URLs <general/urls>`.\n   These can be optionally enabled in your config file.\n-  Added the ability to :doc:`specify a \"suffix\" <general/urls>`, which will be\n   appended to your URLs. For example, you could add .html to your URLs,\n   making them appear static. This feature is enabled in your config\n   file.\n-  Added a new error template for use with native PHP errors.\n-  Added \"alternator\" function in the :doc:`string\n   helpers <./helpers/string_helper>`.\n-  Removed slashing from the input class. After much debate we decided\n   to kill this feature.\n-  Change the commenting style in the scripts to the PEAR standard so\n   that IDEs and tools like phpDocumenter can harvest the comments.\n-  Added better class and function name-spacing to avoid collisions with\n   user developed classes. All CodeIgniter classes are now prefixed with\n   CI\\_ and all controller methods are prefixed with _ci to avoid\n   controller collisions. A list of reserved function names can be\n   :doc:`found here <general/controllers>`.\n-  Redesigned how the \"CI\" super object is referenced, depending on\n   whether PHP 4 or 5 is being run, since PHP 5 allows a more graceful\n   way to manage objects that utilizes a bit less resources.\n-  Deprecated: $this->db->use_table() has been deprecated. Please read\n   the :doc:`Active Record <./database/query_builder>` page for\n   information.\n-  Deprecated: $this->db->smart_escape_str() has been deprecated.\n   Please use this instead: $this->db->escape()\n-  Fixed a bug in the exception handler which was preventing some PHP\n   errors from showing up.\n-  Fixed a typo in the URI class. $this->total_segment() should be\n   plural: $this->total_segments()\n-  Fixed some typos in the default calendar template\n-  Fixed some typos in the user guide\n\nVersion 1.2\n===========\n\nRelease Date: March 21, 2006\n\n-  Redesigned some internal aspects of the framework to resolve scoping\n   problems that surfaced during the beta tests. The problem was most\n   notable when instantiating classes in your constructors, particularly\n   if those classes in turn did work in their constructors.\n-  Added a global function named\n   :doc:`get_instance() <general/ancillary_classes>` allowing the main\n   CodeIgniter object to be accessible throughout your own classes.\n-  Added new :doc:`File Helper <./helpers/file_helper>`:\n   delete_files()\n-  Added new :doc:`URL Helpers <./helpers/url_helper>`: base_url(),\n   index_page()\n-  Added the ability to create your own :doc:`core\n   libraries <general/creating_libraries>` and store them in your local\n   application directory.\n-  Added an overwrite option to the :doc:`Upload\n   class <./libraries/file_uploading>`, enabling files to be\n   overwritten rather than having the file name appended.\n-  Added Javascript Calendar plugin.\n-  Added search feature to user guide. Note: This is done using Google,\n   which at the time of this writing has not crawled all the pages of\n   the docs.\n-  Updated the parser class so that it allows tag pars within other tag\n   pairs.\n-  Fixed a bug in the DB \"where\" function.\n-  Fixed a bug that was preventing custom config files to be\n   auto-loaded.\n-  Fixed a bug in the mysql class bind feature that prevented question\n   marks in the replacement data.\n-  Fixed some bugs in the xss_clean function\n\nVersion Beta 1.1\n================\n\nRelease Date: March 10, 2006\n\n-  Added a :doc:`Calendaring class <./libraries/calendar>`.\n-  Added support for running :doc:`multiple\n   applications <general/managing_apps>` that share a common CodeIgniter\n   backend.\n-  Moved the \"uri protocol\" variable from the index.php file into the\n   config.php file\n-  Fixed a problem that was preventing certain function calls from\n   working within constructors.\n-  Fixed a problem that was preventing the $this->load->library function\n   from working in constructors.\n-  Fixed a bug that occurred when the session class was loaded using the\n   auto-load routine.\n-  Fixed a bug that can happen with PHP versions that do not support the\n   E_STRICT constant\n-  Fixed a data type error in the form_radio function (form helper)\n-  Fixed a bug that was preventing the xss_clean function from being\n   called from the validation class.\n-  Fixed the cookie related config names, which were incorrectly\n   specified as $conf rather than $config\n-  Fixed a pagination problem in the scaffolding.\n-  Fixed a bug in the mysql class \"where\" function.\n-  Fixed a regex problem in some code that trimmed duplicate slashes.\n-  Fixed a bug in the ``br()`` function in the HTML helper\n-  Fixed a syntax mistake in the form_dropdown function in the Form\n   Helper.\n-  Removed the \"style\" attributes form the form helpers.\n-  Updated the documentation. Added \"next/previous\" links to each page\n   and fixed various typos.\n\nVersion Beta 1.0\n================\n\nRelease Date: February 28, 2006\n\nFirst publicly released version.\n"
  },
  {
    "path": "user_guide_src/source/conf.py",
    "content": "# -*- coding: utf-8 -*-\n#\n# CodeIgniter documentation build configuration file, created by\n# sphinx-quickstart on Sun Aug 28 07:24:38 2011.\n#\n# This file is execfile()d with the current directory set to its containing dir.\n#\n# Note that not all possible configuration values are present in this\n# autogenerated file.\n#\n# All configuration values have a default; values that are commented out\n# serve to show the default.\n\nimport sys, os\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#sys.path.insert(0, os.path.abspath('.'))\n\n# -- General configuration -----------------------------------------------------\n\n# If your documentation needs a minimal Sphinx version, state it here.\n#needs_sphinx = '1.0'\n\n# Add any Sphinx extension module names here, as strings. They can be extensions\n# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.\nextensions = ['sphinx.ext.ifconfig', 'sphinxcontrib.phpdomain']\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix of source filenames.\nsource_suffix = '.rst'\n\n# The encoding of source files.\n#source_encoding = 'utf-8-sig'\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# General information about the project.\nproject = u'CodeIgniter'\ncopyright = u'2019 - 2022, CodeIgniter Foundation'\n\n# The version info for the project you're documenting, acts as replacement for\n# |version| and |release|, also used in various other places throughout the\n# built documents.\n#\n# The short X.Y version.\nversion = '3.1.14-dev'\n# The full version, including alpha/beta/rc tags.\nrelease = '3.1.14-dev'\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#language = None\n\n# There are two options for replacing |today|: either, you set today to some\n# non-false value, then it is used:\n#today = ''\n# Else, today_fmt is used as the format for a strftime call.\n#today_fmt = '%B %d, %Y'\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\nexclude_patterns = []\n\n# The reST default role (used for this markup: `text`) to use for all documents.\n#default_role = None\n\n# If true, '()' will be appended to :php:func: etc. cross-reference text.\n#add_function_parentheses = True\n\n# If true, the current module name will be prepended to all description\n# unit titles (such as .. php:function::).\n#add_module_names = True\n\n# If true, sectionauthor and moduleauthor directives will be shown in the\n# output. They are ignored by default.\n#show_authors = False\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = 'trac'\nhighlight_language = 'ci'\n\n# A list of ignored prefixes for module index sorting.\n#modindex_common_prefix = []\n\n\n# -- Options for HTML output ---------------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\n#\nhtml_theme = 'sphinx_rtd_theme'\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\n#\n# Specifying a few options; just a starting point & we can play with it.\nhtml_theme_options = {\n\n}\n\n# Add any paths that contain custom themes here, relative to this directory.\nhtml_theme_path = [\"./_themes\"]\n\n# The name for this set of Sphinx documents.  If None, it defaults to\n# \"<project> v<release> documentation\".\n#html_title = None\n\n# A shorter title for the navigation bar.  Default is the same as html_title.\n#html_short_title = None\n\n# The name of an image file (relative to this directory) to place at the top\n# of the sidebar.\n#html_logo = None\n\n# The name of an image file (within the static path) to use as favicon of the\n# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32\n# pixels large.\nhtml_favicon = 'images/ci-icon.ico'\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\n#html_static_path = ['_static']\n\n# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,\n# using the given strftime format.\nhtml_last_updated_fmt = '%b %d, %Y'\n\n# If true, SmartyPants will be used to convert quotes and dashes to\n# typographically correct entities.\n#html_use_smartypants = True\n\n# Custom sidebar templates, maps document names to template names.\n#html_sidebars = {}\n\n# Additional templates that should be rendered to pages, maps page names to\n# template names.\n#html_additional_pages = {}\n\n# If false, no module index is generated.\n#html_domain_indices = True\n\n# If false, no index is generated.\n#html_use_index = True\n\n# If true, the index is split into individual pages for each letter.\n#html_split_index = False\n\n# If true, links to the reST sources are added to the pages.\n#html_show_sourcelink = True\n\n# If true, \"Created using Sphinx\" is shown in the HTML footer. Default is True.\n#html_show_sphinx = True\n\n# If true, \"(C) Copyright ...\" is shown in the HTML footer. Default is True.\n#html_show_copyright = True\n\n# If true, an OpenSearch description file will be output, and all pages will\n# contain a <link> tag referring to it.  The value of this option must be the\n# base URL from which the finished HTML is served.\n#html_use_opensearch = ''\n\n# This is the file name suffix for HTML files (e.g. \".xhtml\").\n#html_file_suffix = None\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = 'CodeIgniterdoc'\n\nhtml_copy_source = False\n\n# -- Options for LaTeX output --------------------------------------------------\n\n# The paper size ('letter' or 'a4').\n#latex_paper_size = 'letter'\n\n# The font size ('10pt', '11pt' or '12pt').\n#latex_font_size = '10pt'\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title, author, documentclass [howto/manual]).\nlatex_documents = [\n  ('index', 'CodeIgniter.tex', u'CodeIgniter Documentation',\n   u'British Columbia Institute of Technology', 'manual'),\n]\n\n# The name of an image file (relative to this directory) to place at the top of\n# the title page.\n#latex_logo = None\n\n# For \"manual\" documents, if this is true, then toplevel headings are parts,\n# not chapters.\n#latex_use_parts = False\n\n# If true, show page references after internal links.\n#latex_show_pagerefs = False\n\n# If true, show URL addresses after external links.\n#latex_show_urls = False\n\n# Additional stuff for the LaTeX preamble.\n#latex_preamble = ''\n\n# Documents to append as an appendix to all manuals.\n#latex_appendices = []\n\n# If false, no module index is generated.\n#latex_domain_indices = True\n\n\n# -- Options for manual page output --------------------------------------------\n\n# One entry per manual page. List of tuples\n# (source start file, name, description, authors, manual section).\nman_pages = [\n    ('index', 'codeigniter', u'CodeIgniter Documentation',\n     [u'British Columbia Institute of Technology'], 1)\n]\n\n\n# -- Options for Epub output ---------------------------------------------------\n\n# Bibliographic Dublin Core info.\nepub_title = u'CodeIgniter'\nepub_author = u'British Columbia Institute of Technology'\nepub_publisher = u'British Columbia Institute of Technology'\nepub_copyright = u'2014 - 2019, British Columbia Institute of Technology'\n\n# The language of the text. It defaults to the language option\n# or en if the language is not set.\n#epub_language = ''\n\n# The scheme of the identifier. Typical schemes are ISBN or URL.\n#epub_scheme = ''\n\n# The unique identifier of the text. This can be a ISBN number\n# or the project homepage.\n#epub_identifier = ''\n\n# A unique identification for the text.\n#epub_uid = ''\n\n# HTML files that should be inserted before the pages created by sphinx.\n# The format is a list of tuples containing the path and title.\n#epub_pre_files = []\n\n# HTML files shat should be inserted after the pages created by sphinx.\n# The format is a list of tuples containing the path and title.\n#epub_post_files = []\n\n# A list of files that should not be packed into the epub file.\n#epub_exclude_files = []\n\n# The depth of the table of contents in toc.ncx.\n#epub_tocdepth = 3\n\n# Allow duplicate toc entries.\n#epub_tocdup = True\n"
  },
  {
    "path": "user_guide_src/source/contributing/index.rst",
    "content": "###########################\nContributing to CodeIgniter\n###########################\n\n.. toctree::\n\t:titlesonly:\n\n\t../documentation/index\n\t../DCO\n\nCodeIgniter is a community driven project and accepts contributions of code\nand documentation from the community. These contributions are made in the form\nof Issues or `Pull Requests <https://help.github.com/articles/using-pull-requests/>`_ \non the `CodeIgniter repository <https://github.com/bcit-ci/CodeIgniter>`_ on GitHub.\n\nIssues are a quick way to point out a bug. If you find a bug or documentation\nerror in CodeIgniter then please check a few things first:\n\n- There is not already an open Issue\n- The issue has already been fixed (check the develop branch, or look for\n  closed Issues)\n- Is it something really obvious that you fix it yourself?\n\nReporting issues is helpful but an even better approach is to send a Pull\nRequest, which is done by \"Forking\" the main repository and committing to your\nown copy. This will require you to use the version control system called Git.\n\n*******\nSupport\n*******\n\nPlease note that GitHub is not for general support questions! If you are\nhaving trouble using a feature of CodeIgniter, ask for help on our\n`forums <https://forum.codeigniter.com/>`_ instead.\n\nIf you are not sure whether you are using something correctly or if you\nhave found a bug, again - please ask on the forums first.\n\n********\nSecurity\n********\n\nDid you find a security issue in CodeIgniter?\n\nPlease *don't* disclose it publicly, but e-mail us at security@codeigniter.com,\nor report it via our page on `HackerOne <https://hackerone.com/codeigniter>`_.\n\nIf you've found a critical vulnerability, we'd be happy to credit you in our\n`ChangeLog <../changelog>`.\n\n****************************\nTips for a Good Issue Report\n****************************\n\nUse a descriptive subject line (eg parser library chokes on commas) rather than a vague one (eg. your code broke).\n\nAddress a single issue in a report.\n\nIdentify the CodeIgniter version (eg 3.0-develop) and the component if you know it (eg. parser library)\n\nExplain what you expected to happen, and what did happen.\nInclude error messages and stacktrace, if any.\n\nInclude short code segments if they help to explain.\nUse a pastebin or dropbox facility to include longer segments of code or screenshots - do not include them in the issue report itself.\nThis means setting a reasonable expiry for those, until the issue is resolved or closed.\n\nIf you know how to fix the issue, you can do so in your own fork & branch, and submit a pull request.\nThe issue report information above should be part of that.\n\nIf your issue report can describe the steps to reproduce the problem, that is great.\nIf you can include a unit test that reproduces the problem, that is even better, as it gives whoever is fixing\nit a clearer target!\n\n\n**********\nGuidelines\n**********\n\nBefore we look into how, here are the guidelines. If your Pull Requests fail\nto pass these guidelines it will be declined and you will need to re-submit\nwhen you’ve made the changes. This might sound a bit tough, but it is required\nfor us to maintain quality of the code-base.\n\nPHP Style\n=========\n\nAll code must meet the `Style Guide\n<https://codeigniter.com/userguide3/general/styleguide.html>`_, which is\nessentially the `Allman indent style\n<https://en.wikipedia.org/wiki/Indent_style#Allman_style>`_, underscores and\nreadable operators. This makes certain that all code is the same format as the\nexisting code and means it will be as readable as possible.\n\nDocumentation\n=============\n\nIf you change anything that requires a change to documentation then you will\nneed to add it. New classes, methods, parameters, changing default values, etc\nare all things that will require a change to documentation. The change-log\nmust also be updated for every change. Also PHPDoc blocks must be maintained.\n\nCompatibility\n=============\n\nCodeIgniter recommends PHP 5.6 or newer to be used, but it should be\ncompatible with PHP 5.4.8 so all code supplied must stick to this\nrequirement. If PHP 5.5 (and above) functions or features are used then\nthere must be a fallback for PHP 5.4.8.\n\nBranching\n=========\n\nCodeIgniter uses the `Git-Flow\n<https://nvie.com/posts/a-successful-git-branching-model/>`_ branching model\nwhich requires all pull requests to be sent to the \"develop\" branch. This is\nwhere the next planned version will be developed. The \"master\" branch will\nalways contain the latest stable version and is kept clean so a \"hotfix\" (e.g:\nan emergency security patch) can be applied to master to create a new version,\nwithout worrying about other features holding it up. For this reason all\ncommits need to be made to \"develop\" and any sent to \"master\" will be closed\nautomatically. If you have multiple changes to submit, please place all\nchanges into their own branch on your fork.\n\nOne thing at a time: A pull request should only contain one change. That does\nnot mean only one commit, but one change - however many commits it took. The\nreason for this is that if you change X and Y but send a pull request for both\nat the same time, we might really want X but disagree with Y, meaning we\ncannot merge the request. Using the Git-Flow branching model you can create\nnew branches for both of these features and send two requests.\n\nSigning\n=======\nYou must sign your work, certifying that you either wrote the work or\notherwise have the right to pass it on to an open source project. git makes\nthis trivial as you merely have to use `--signoff` on your commits to your\nCodeIgniter fork.\n\n.. code-block:: bash\n\n\tgit commit --signoff\n\nor simply\n\n.. code-block:: bash\n\n\tgit commit -s\n\nThis will sign your commits with the information setup in your git config, e.g.\n\n\tSigned-off-by: John Q Public <john.public@example.com>\n\nIf you are using Tower there is a \"Sign-Off\" checkbox in the commit window. You\ncould even alias git commit to use the -s flag so you don’t have to think about\nit.\n\nBy signing your work in this manner, you certify to a \"Developer's Certificate\nof Origin\". The current version of this certificate is in the :doc:`/DCO` file\nin the root of this documentation.\n"
  },
  {
    "path": "user_guide_src/source/database/caching.rst",
    "content": "######################\nDatabase Caching Class\n######################\n\nThe Database Caching Class permits you to cache your queries as text\nfiles for reduced database load.\n\n.. important:: This class is initialized automatically by the database\n\tdriver when caching is enabled. Do NOT load this class manually.\n\n.. important:: Not all query result functions are available when you\n\tuse caching. Please read this page carefully.\n\nEnabling Caching\n================\n\nCaching is enabled in three steps:\n\n-  Create a writable directory on your server where the cache files can\n   be stored.\n-  Set the path to your cache folder in your\n   application/config/database.php file.\n-  Enable the caching feature, either globally by setting the preference\n   in your application/config/database.php file, or manually as\n   described below.\n\nOnce enabled, caching will happen automatically whenever a page is\nloaded that contains database queries.\n\nHow Does Caching Work?\n======================\n\nCodeIgniter's query caching system happens dynamically when your pages\nare viewed. When caching is enabled, the first time a web page is\nloaded, the query result object will be serialized and stored in a text\nfile on your server. The next time the page is loaded the cache file\nwill be used instead of accessing your database. Your database usage can\neffectively be reduced to zero for any pages that have been cached.\n\nOnly read-type (SELECT) queries can be cached, since these are the only\ntype of queries that produce a result. Write-type (INSERT, UPDATE, etc.)\nqueries, since they don't generate a result, will not be cached by the\nsystem.\n\nCache files DO NOT expire. Any queries that have been cached will remain\ncached until you delete them. The caching system permits you clear\ncaches associated with individual pages, or you can delete the entire\ncollection of cache files. Typically you'll want to use the housekeeping\nfunctions described below to delete cache files after certain events\ntake place, like when you've added new information to your database.\n\nWill Caching Improve Your Site's Performance?\n=============================================\n\nGetting a performance gain as a result of caching depends on many\nfactors. If you have a highly optimized database under very little load,\nyou probably won't see a performance boost. If your database is under\nheavy use you probably will see an improved response, assuming your\nfile-system is not overly taxed. Remember that caching simply changes\nhow your information is retrieved, shifting it from being a database\noperation to a file-system one.\n\nIn some clustered server environments, for example, caching may be\ndetrimental since file-system operations are so intense. On single\nservers in shared environments, caching will probably be beneficial.\nUnfortunately there is no single answer to the question of whether you\nshould cache your database. It really depends on your situation.\n\nHow are Cache Files Stored?\n===========================\n\nCodeIgniter places the result of EACH query into its own cache file.\nSets of cache files are further organized into sub-folders corresponding\nto your controller functions. To be precise, the sub-folders are named\nidentically to the first two segments of your URI (the controller class\nname and function name).\n\nFor example, let's say you have a controller called blog with a function\ncalled comments that contains three queries. The caching system will\ncreate a cache folder called blog+comments, into which it will write\nthree cache files.\n\nIf you use dynamic queries that change based on information in your URI\n(when using pagination, for example), each instance of the query will\nproduce its own cache file. It's possible, therefore, to end up with\nmany times more cache files than you have queries.\n\nManaging your Cache Files\n=========================\n\nSince cache files do not expire, you'll need to build deletion routines\ninto your application. For example, let's say you have a blog that\nallows user commenting. Whenever a new comment is submitted you'll want\nto delete the cache files associated with the controller function that\nserves up your comments. You'll find two delete functions described\nbelow that help you clear data.\n\nNot All Database Functions Work with Caching\n============================================\n\nLastly, we need to point out that the result object that is cached is a\nsimplified version of the full result object. For that reason, some of\nthe query result functions are not available for use.\n\nThe following functions ARE NOT available when using a cached result\nobject:\n\n-  num_fields()\n-  field_names()\n-  field_data()\n-  free_result()\n\nAlso, the two database resources (result_id and conn_id) are not\navailable when caching, since result resources only pertain to run-time\noperations.\n\n******************\nFunction Reference\n******************\n\n$this->db->cache_on() / $this->db->cache_off()\n================================================\n\nManually enables/disables caching. This can be useful if you want to\nkeep certain queries from being cached. Example::\n\n\t// Turn caching on\n\t$this->db->cache_on();\n\t$query = $this->db->query(\"SELECT * FROM mytable\");\n\t\n\t// Turn caching off for this one query\n\t$this->db->cache_off();\n\t$query = $this->db->query(\"SELECT * FROM members WHERE member_id = '$current_user'\");\n\t\n\t// Turn caching back on\n\t$this->db->cache_on();\n\t$query = $this->db->query(\"SELECT * FROM another_table\");\n\n$this->db->cache_delete()\n==========================\n\nDeletes the cache files associated with a particular page. This is\nuseful if you need to clear caching after you update your database.\n\nThe caching system saves your cache files to folders that correspond to\nthe URI of the page you are viewing. For example, if you are viewing a\npage at example.com/index.php/blog/comments, the caching system will put\nall cache files associated with it in a folder called blog+comments. To\ndelete those particular cache files you will use::\n\n\t$this->db->cache_delete('blog', 'comments');\n\nIf you do not use any parameters the current URI will be used when\ndetermining what should be cleared.\n\n$this->db->cache_delete_all()\n===============================\n\nClears all existing cache files. Example::\n\n\t$this->db->cache_delete_all();\n\n"
  },
  {
    "path": "user_guide_src/source/database/call_function.rst",
    "content": "#####################\nCustom Function Calls\n#####################\n\n$this->db->call_function();\n============================\n\nThis function enables you to call PHP database functions that are not\nnatively included in CodeIgniter, in a platform independent manner. For\nexample, let's say you want to call the mysql_get_client_info()\nfunction, which is **not** natively supported by CodeIgniter. You could\ndo so like this::\n\n\t$this->db->call_function('get_client_info');\n\nYou must supply the name of the function, **without** the mysql\\_\nprefix, in the first parameter. The prefix is added automatically based\non which database driver is currently being used. This permits you to\nrun the same function on different database platforms. Obviously not all\nfunction calls are identical between platforms, so there are limits to\nhow useful this function can be in terms of portability.\n\nAny parameters needed by the function you are calling will be added to\nthe second parameter.\n\n::\n\n\t$this->db->call_function('some_function', $param1, $param2, etc..);\n\nOften, you will either need to supply a database connection ID or a\ndatabase result ID. The connection ID can be accessed using::\n\n\t$this->db->conn_id;\n\nThe result ID can be accessed from within your result object, like this::\n\n\t$query = $this->db->query(\"SOME QUERY\");\n\t\n\t$query->result_id;"
  },
  {
    "path": "user_guide_src/source/database/configuration.rst",
    "content": "######################\nDatabase Configuration\n######################\n\nCodeIgniter has a config file that lets you store your database\nconnection values (username, password, database name, etc.). The config\nfile is located at application/config/database.php. You can also set\ndatabase connection values for specific\n:doc:`environments <../libraries/config>` by placing **database.php**\nin the respective environment config folder.\n\nThe config settings are stored in a multi-dimensional array with this\nprototype::\n\n\t$db['default'] = array(\n\t\t'dsn'\t=> '',\n\t\t'hostname' => 'localhost',\n\t\t'username' => 'root',\n\t\t'password' => '',\n\t\t'database' => 'database_name',\n\t\t'dbdriver' => 'mysqli',\n\t\t'dbprefix' => '',\n\t\t'pconnect' => TRUE,\n\t\t'db_debug' => TRUE,\n\t\t'cache_on' => FALSE,\n\t\t'cachedir' => '',\n\t\t'char_set' => 'utf8',\n\t\t'dbcollat' => 'utf8_general_ci',\n\t\t'swap_pre' => '',\n\t\t'encrypt' => FALSE,\n\t\t'compress' => FALSE,\n\t\t'stricton' => FALSE,\n\t\t'failover' => array()\n\t);\n\nSome database drivers (such as PDO, PostgreSQL, Oracle, ODBC) might\nrequire a full DSN string to be provided. If that is the case, you\nshould use the 'dsn' configuration setting, as if you're using the\ndriver's underlying native PHP extension, like this::\n\n\t// PDO\n\t$db['default']['dsn'] = 'pgsql:host=localhost;port=5432;dbname=database_name';\n\n\t// Oracle\n\t$db['default']['dsn'] = '//localhost/XE';\n\n.. note:: If you do not specify a DSN string for a driver that requires it, CodeIgniter\n\twill try to build it with the rest of the provided settings.\n\n.. note:: If you provide a DSN string and it is missing some valid settings (e.g. the\n\tdatabase character set), which are present in the rest of the configuration\n\tfields, CodeIgniter will append them.\n\nYou can also specify failovers for the situation when the main connection cannot connect for some reason.\nThese failovers can be specified by setting the failover for a connection like this::\n\n\t$db['default']['failover'] = array(\n\t\t\tarray(\n\t\t\t\t'hostname' => 'localhost1',\n\t\t\t\t'username' => '',\n\t\t\t\t'password' => '',\n\t\t\t\t'database' => '',\n\t\t\t\t'dbdriver' => 'mysqli',\n\t\t\t\t'dbprefix' => '',\n\t\t\t\t'pconnect' => TRUE,\n\t\t\t\t'db_debug' => TRUE,\n\t\t\t\t'cache_on' => FALSE,\n\t\t\t\t'cachedir' => '',\n\t\t\t\t'char_set' => 'utf8',\n\t\t\t\t'dbcollat' => 'utf8_general_ci',\n\t\t\t\t'swap_pre' => '',\n\t\t\t\t'encrypt' => FALSE,\n\t\t\t\t'compress' => FALSE,\n\t\t\t\t'stricton' => FALSE\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'hostname' => 'localhost2',\n\t\t\t\t'username' => '',\n\t\t\t\t'password' => '',\n\t\t\t\t'database' => '',\n\t\t\t\t'dbdriver' => 'mysqli',\n\t\t\t\t'dbprefix' => '',\n\t\t\t\t'pconnect' => TRUE,\n\t\t\t\t'db_debug' => TRUE,\n\t\t\t\t'cache_on' => FALSE,\n\t\t\t\t'cachedir' => '',\n\t\t\t\t'char_set' => 'utf8',\n\t\t\t\t'dbcollat' => 'utf8_general_ci',\n\t\t\t\t'swap_pre' => '',\n\t\t\t\t'encrypt' => FALSE,\n\t\t\t\t'compress' => FALSE,\n\t\t\t\t'stricton' => FALSE\n\t\t\t)\n\t\t);\n\nYou can specify as many failovers as you like.\n\nThe reason we use a multi-dimensional array rather than a more simple\none is to permit you to optionally store multiple sets of connection\nvalues. If, for example, you run multiple environments (development,\nproduction, test, etc.) under a single installation, you can set up a\nconnection group for each, then switch between groups as needed. For\nexample, to set up a \"test\" environment you would do this::\n\n\t$db['test'] = array(\n\t\t'dsn'\t=> '',\n\t\t'hostname' => 'localhost',\n\t\t'username' => 'root',\n\t\t'password' => '',\n\t\t'database' => 'database_name',\n\t\t'dbdriver' => 'mysqli',\n\t\t'dbprefix' => '',\n\t\t'pconnect' => TRUE,\n\t\t'db_debug' => TRUE,\n\t\t'cache_on' => FALSE,\n\t\t'cachedir' => '',\n\t\t'char_set' => 'utf8',\n\t\t'dbcollat' => 'utf8_general_ci',\n\t\t'swap_pre' => '',\n\t\t'compress' => FALSE,\n\t\t'encrypt' => FALSE,\n\t\t'stricton' => FALSE,\n\t\t'failover' => array()\n\t);\n\nThen, to globally tell the system to use that group you would set this\nvariable located in the config file::\n\n\t$active_group = 'test';\n\n.. note:: The name 'test' is arbitrary. It can be anything you want. By\n\tdefault we've used the word \"default\" for the primary connection,\n\tbut it too can be renamed to something more relevant to your project.\n\nExplanation of Values:\n----------------------\n\n======================  ===========================================================================================================\n Name Config             Description\n======================  ===========================================================================================================\n**dsn**\t\t\tThe DSN connect string (an all-in-one configuration sequence).\n**hostname**\t\tThe hostname of your database server. Often this is 'localhost'.\n**username**\t\tThe username used to connect to the database.\n**password**\t\tThe password used to connect to the database.\n**database**\t\tThe name of the database you want to connect to.\n**dbdriver**\t\tThe database type. ie: mysqli, postgre, odbc, etc. Must be specified in lower case.\n**dbprefix**\t\tAn optional table prefix which will added to the table name when running\n\t\t\t:doc:`Query Builder <query_builder>` queries. This permits multiple CodeIgniter\n\t\t\tinstallations to share one database.\n**pconnect**\t\tTRUE/FALSE (boolean) - Whether to use a persistent connection.\n**db_debug**\t\tTRUE/FALSE (boolean) - Whether database errors should be displayed.\n**cache_on**\t\tTRUE/FALSE (boolean) - Whether database query caching is enabled,\n\t\t\tsee also :doc:`Database Caching Class <caching>`.\n**cachedir**\t\tThe absolute server path to your database query cache directory.\n**char_set**\t\tThe character set used in communicating with the database.\n**dbcollat**\t\tThe character collation used in communicating with the database\n\n\t\t\t.. note:: Only used in the 'mysql' and 'mysqli' drivers.\n\n**swap_pre**\t\tA default table prefix that should be swapped with dbprefix. This is useful for distributed\n\t\t\tapplications where you might run manually written queries, and need the prefix to still be\n\t\t\tcustomizable by the end user.\n**schema**\t\tThe database schema, defaults to 'public'. Used by PostgreSQL and ODBC drivers.\n**encrypt**\t\tWhether or not to use an encrypted connection.\n\n\t\t\t  - 'mysql' (deprecated), 'sqlsrv' and 'pdo/sqlsrv' drivers accept TRUE/FALSE\n\t\t\t  - 'mysqli' and 'pdo/mysql' drivers accept an array with the following options:\n\t\t\t  \n\t\t\t    - 'ssl_key'    - Path to the private key file\n\t\t\t    - 'ssl_cert'   - Path to the public key certificate file\n\t\t\t    - 'ssl_ca'     - Path to the certificate authority file\n\t\t\t    - 'ssl_capath' - Path to a directory containing trusted CA certificates in PEM format\n\t\t\t    - 'ssl_cipher' - List of *allowed* ciphers to be used for the encryption, separated by colons (':')\n\t\t\t    - 'ssl_verify' - TRUE/FALSE; Whether to verify the server certificate or not ('mysqli' only)\n\n**compress**\t\tWhether or not to use client compression (MySQL only).\n**stricton**\t\tTRUE/FALSE (boolean) - Whether to force \"Strict Mode\" connections, good for ensuring strict SQL\n\t\t\twhile developing an application.\n**port**\t\tThe database port number. To use this value you have to add a line to the database config array.\n\t\t\t::\n\n\t\t\t\t$db['default']['port'] = 5432;\n\n======================  ===========================================================================================================\n\n.. note:: Depending on what database platform you are using (MySQL, PostgreSQL,\n\tetc.) not all values will be needed. For example, when using SQLite you\n\twill not need to supply a username or password, and the database name\n\twill be the path to your database file. The information above assumes\n\tyou are using MySQL.\n"
  },
  {
    "path": "user_guide_src/source/database/connecting.rst",
    "content": "###########################\nConnecting to your Database\n###########################\n\nThere are two ways to connect to a database:\n\nAutomatically Connecting\n========================\n\nThe \"auto connect\" feature will load and instantiate the database class\nwith every page load. To enable \"auto connecting\", add the word database\nto the library array, as indicated in the following file:\n\napplication/config/autoload.php\n\nManually Connecting\n===================\n\nIf only some of your pages require database connectivity you can\nmanually connect to your database by adding this line of code in any\nfunction where it is needed, or in your class constructor to make the\ndatabase available globally in that class.\n\n::\n\n\t$this->load->database();\n\nIf the above function does **not** contain any information in the first\nparameter it will connect to the group specified in your database config\nfile. For most people, this is the preferred method of use.\n\nAvailable Parameters\n--------------------\n\n#. The database connection values, passed either as an array or a DSN\n   string.\n#. TRUE/FALSE (boolean). Whether to return the connection ID (see\n   Connecting to Multiple Databases below).\n#. TRUE/FALSE (boolean). Whether to enable the Query Builder class. Set\n   to TRUE by default.\n\nManually Connecting to a Database\n---------------------------------\n\nThe first parameter of this function can **optionally** be used to\nspecify a particular database group from your config file, or you can\neven submit connection values for a database that is not specified in\nyour config file. Examples:\n\nTo choose a specific group from your config file you can do this::\n\n\t$this->load->database('group_name');\n\nWhere group_name is the name of the connection group from your config\nfile.\n\nTo connect manually to a desired database you can pass an array of\nvalues::\n\n\t$config['hostname'] = 'localhost';\n\t$config['username'] = 'myusername';\n\t$config['password'] = 'mypassword';\n\t$config['database'] = 'mydatabase';\n\t$config['dbdriver'] = 'mysqli';\n\t$config['dbprefix'] = '';\n\t$config['pconnect'] = FALSE;\n\t$config['db_debug'] = TRUE;\n\t$config['cache_on'] = FALSE;\n\t$config['cachedir'] = '';\n\t$config['char_set'] = 'utf8';\n\t$config['dbcollat'] = 'utf8_general_ci';\n\t$this->load->database($config);\n\nFor information on each of these values please see the :doc:`configuration\npage <configuration>`.\n\n.. note:: For the PDO driver, you should use the $config['dsn'] setting\n\tinstead of 'hostname' and 'database':\n\n\t|\n\t| $config['dsn'] = 'mysql:host=localhost;dbname=mydatabase';\n\nOr you can submit your database values as a Data Source Name. DSNs must\nhave this prototype::\n\n\t$dsn = 'dbdriver://username:password@hostname/database';  \n\t$this->load->database($dsn);\n\nTo override default config values when connecting with a DSN string, add\nthe config variables as a query string.\n\n::\n\n\t$dsn = 'dbdriver://username:password@hostname/database?char_set=utf8&dbcollat=utf8_general_ci&cache_on=true&cachedir=/path/to/cache';  \n\t$this->load->database($dsn);\n\nConnecting to Multiple Databases\n================================\n\nIf you need to connect to more than one database simultaneously you can\ndo so as follows::\n\n\t$DB1 = $this->load->database('group_one', TRUE); \n\t$DB2 = $this->load->database('group_two', TRUE);\n\nNote: Change the words \"group_one\" and \"group_two\" to the specific\ngroup names you are connecting to (or you can pass the connection values\nas indicated above).\n\nBy setting the second parameter to TRUE (boolean) the function will\nreturn the database object.\n\n.. note:: When you connect this way, you will use your object name to issue\n\tcommands rather than the syntax used throughout this guide. In other\n\twords, rather than issuing commands with:\n\t\n\t|\n\t| $this->db->query();\n\t| $this->db->result();\n\t| etc...\n\t|\n\t| You will instead use:\n\t|\n\t| $DB1->query();\n\t| $DB1->result();\n\t| etc...\n\n.. note:: You don't need to create separate database configurations if you\n\tonly need to use a different database on the same connection. You\n\tcan switch to a different database when you need to, like this:\n\n\t| $this->db->db_select($database2_name);\n\nReconnecting / Keeping the Connection Alive\n===========================================\n\nIf the database server's idle timeout is exceeded while you're doing\nsome heavy PHP lifting (processing an image, for instance), you should\nconsider pinging the server by using the reconnect() method before\nsending further queries, which can gracefully keep the connection alive\nor re-establish it.\n\n::\n\n\t$this->db->reconnect();\n\nManually closing the Connection\n===============================\n\nWhile CodeIgniter intelligently takes care of closing your database\nconnections, you can explicitly close the connection.\n\n::\n\n\t$this->db->close();"
  },
  {
    "path": "user_guide_src/source/database/db_driver_reference.rst",
    "content": "###################\nDB Driver Reference\n###################\n\nThis is the platform-independent base DB implementation class.\nThis class will not be called directly. Rather, the adapter\nclass for the specific database will extend and instantiate it.\n\nThe how-to material for this has been split over several articles.\nThis article is intended to be a reference for them.\n\n.. important:: Not all methods are supported by all database drivers,\n\tsome of them may fail (and return FALSE) if the underlying\n\tdriver does not support them.\n\n.. php:class:: CI_DB_driver\n\n\t.. php:method:: initialize()\n\n\t\t:rtype:\tvoid\n\t\t:throws:\tRuntimeException\tIn case of failure\n\n\t\tInitialize database settings, establish a connection to\n\t\tthe database.\n\n\t.. php:method:: db_connect($persistent = TRUE)\n\n\t\t:param\tbool\t$persistent: Whether to establish a persistent connection or a regular one\n\t\t:returns:\tDatabase connection resource/object or FALSE on failure\n\t\t:rtype:\tmixed\n\n\t\tEstablish a connection with the database.\n\n\t\t.. note:: The returned value depends on the underlying\n\t\t\tdriver in use. For example, a ``mysqli`` instance\n\t\t\twill be returned with the 'mysqli' driver.\n\n\t.. php:method:: db_pconnect()\n\n\t\t:returns:\tDatabase connection resource/object or FALSE on failure\n\t\t:rtype:\tmixed\n\n\t\tEstablish a persistent connection with the database.\n\n\t\t.. note:: This method is just an alias for ``db_connect(TRUE)``.\n\n\t.. php:method:: reconnect()\n\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tKeep / reestablish the database connection if no queries\n\t\thave been sent for a length of time exceeding the\n\t\tserver's idle timeout.\n\n\t.. php:method:: db_select([$database = ''])\n\n\t\t:param\tstring\t$database: Database name\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tSelect / switch the current database.\n\n\t.. php:method:: platform()\n\n\t\t:returns:\tPlatform name\n\t\t:rtype:\tstring\n\n\t\tThe name of the platform in use (mysql, mssql, etc...).\n\n\t.. php:method:: version()\n\n\t\t:returns:\tThe version of the database being used\n\t\t:rtype:\tstring\n\n\t\tDatabase version number.\n\n\t.. php:method:: query($sql[, $binds = FALSE[, $return_object = NULL]])\n\n\t\t:param\tstring\t$sql: The SQL statement to execute\n\t\t:param\tarray\t$binds: An array of binding data\n\t\t:param\tbool\t$return_object: Whether to return a result object or not\n\t\t:returns:\tTRUE for successful \"write-type\" queries, CI_DB_result instance (method chaining) on \"query\" success, FALSE on failure\n\t\t:rtype:\tmixed\n\n\t\tExecute an SQL query.\n\n\t\tAccepts an SQL string as input and returns a result object\n\t\tupon successful execution of a \"read\" type query.\n\n\t\tReturns:\n\n\t\t   - Boolean TRUE upon successful execution of a \"write type\" queries\n\t\t   - Boolean FALSE upon failure\n\t\t   - ``CI_DB_result`` object for \"read type\" queries\n\n\t\t.. note: If 'db_debug' setting is set to TRUE, an error\n\t\t\tpage will be displayed instead of returning FALSE\n\t\t\ton failures and script execution will stop.\n\n\t.. php:method:: simple_query($sql)\n\n\t\t:param\tstring\t$sql: The SQL statement to execute\n\t\t:returns:\tWhatever the underlying driver's \"query\" function returns\n\t\t:rtype:\tmixed\n\n\t\tA simplified version of the ``query()`` method, appropriate\n\t\tfor use when you don't need to get a result object or to\n\t\tjust send a query to the database and not care for the result.\n\n\t.. php:method:: affected_rows()\n\n\t\t:returns:\tNumber of rows affected\n\t\t:rtype:\tint\n\n\t\tReturns the number of rows *changed* by the last executed query.\n\n\t\tUseful for checking how much rows were created, updated or deleted\n\t\tduring the last executed query.\n\n\t.. php:method:: trans_strict([$mode = TRUE])\n\n\t\t:param\tbool\t$mode: Strict mode flag\n\t\t:rtype:\tvoid\n\n\t\tEnable/disable transaction \"strict\" mode.\n\n\t\tWhen strict mode is enabled, if you are running multiple\n\t\tgroups of transactions and one group fails, all subsequent\n\t\tgroups will be rolled back.\n\n\t\tIf strict mode is disabled, each group is treated\n\t\tautonomously, meaning a failure of one group will not\n\t\taffect any others.\n\n\t.. php:method:: trans_off()\n\n\t\t:rtype:\tvoid\n\n\t\tDisables transactions at run-time.\n\n\t.. php:method:: trans_start([$test_mode = FALSE])\n\n\t\t:param\tbool\t$test_mode: Test mode flag\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tStart a transaction.\n\n\t.. php:method:: trans_complete()\n\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tComplete Transaction.\n\n\t.. php:method:: trans_status()\n\n\t\t:returns:\tTRUE if the transaction succeeded, FALSE if it failed\n\t\t:rtype:\tbool\n\n\t\tLets you retrieve the transaction status flag to\n\t\tdetermine if it has failed.\n\t\t\n\t.. php:method:: trans_active()\n\n\t\t:returns:\tTRUE if a transaction is active, FALSE if not\n\t\t:rtype:\tbool\n\n\t\tDetermines if a transaction is currently active.\n\n\t.. php:method:: compile_binds($sql, $binds)\n\n\t\t:param\tstring\t$sql: The SQL statement \n\t\t:param\tarray\t$binds: An array of binding data\n\t\t:returns:\tThe updated SQL statement\n\t\t:rtype:\tstring\n\n\t\tCompiles an SQL query with the bind values passed for it.\n\n\t.. php:method:: is_write_type($sql)\n\n\t\t:param\tstring\t$sql: The SQL statement \n\t\t:returns:\tTRUE if the SQL statement is of \"write type\", FALSE if not\n\t\t:rtype:\tbool\n\n\t\tDetermines if a query is of a \"write\" type (such as\n\t\tINSERT, UPDATE, DELETE) or \"read\" type (i.e. SELECT).\n\n\t.. php:method:: elapsed_time([$decimals = 6])\n\n\t\t:param\tint\t$decimals: The number of decimal places\n\t\t:returns:\tThe aggregate query elapsed time, in microseconds\n\t\t:rtype:\tstring\n\n\t\tCalculate the aggregate query elapsed time.\n\n\t.. php:method:: total_queries()\n\n\t\t:returns:\tThe total number of queries executed\n\t\t:rtype:\tint\n\n\t\tReturns the total number of queries that have been\n\t\texecuted so far.\n\n\t.. php:method:: last_query()\n\n\t\t:returns:\tThe last query executed\n\t\t:rtype:\tstring\n\n\t\tReturns the last query that was executed.\n\n\t.. php:method:: escape($str)\n\n\t\t:param\tmixed\t$str: The value to escape, or an array of multiple ones\n\t\t:returns:\tThe escaped value(s)\n\t\t:rtype:\tmixed\n\n\t\tEscapes input data based on type, including boolean and\n\t\tNULLs.\n\n\t.. php:method:: escape_str($str[, $like = FALSE])\n\n\t\t:param\tmixed\t$str: A string value or array of multiple ones\n\t\t:param\tbool\t$like: Whether or not the string will be used in a LIKE condition\n\t\t:returns:\tThe escaped string(s)\n\t\t:rtype:\tmixed\n\n\t\tEscapes string values.\n\n\t\t.. warning:: The returned strings do NOT include quotes\n\t\t\taround them.\n\n\t.. php:method:: escape_like_str($str)\n\n\t\t:param\tmixed\t$str: A string value or array of multiple ones\n\t\t:returns:\tThe escaped string(s)\n\t\t:rtype:\tmixed\n\n\t\tEscape LIKE strings.\n\n\t\tSimilar to ``escape_str()``, but will also escape the ``%``\n\t\tand ``_`` wildcard characters, so that they don't cause\n\t\tfalse-positives in LIKE conditions.\n\n\t\t.. important:: The ``escape_like_str()`` method uses '!' (exclamation mark)\n\t\t\tto escape special characters for *LIKE* conditions. Because this\n\t\t\tmethod escapes partial strings that you would wrap in quotes\n\t\t\tyourself, it cannot automatically add the ``ESCAPE '!'``\n\t\t\tcondition for you, and so you'll have to manually do that.\n\n\n\t.. php:method:: primary($table)\n\n\t\t:param\tstring\t$table: Table name\n\t\t:returns:\tThe primary key name, FALSE if none\n\t\t:rtype:\tstring\n\n\t\tRetrieves the primary key of a table.\n\n\t\t.. note:: If the database platform does not support primary\n\t\t\tkey detection, the first column name may be assumed\n\t\t\tas the primary key.\n\n\t.. php:method:: count_all([$table = ''])\n\n\t\t:param\tstring\t$table: Table name\n\t\t:returns:\tRow count for the specified table\n\t\t:rtype:\tint\n\n\t\tReturns the total number of rows in a table, or 0 if no\n\t\ttable was provided.\n\n\t.. php:method:: list_tables([$constrain_by_prefix = FALSE])\n\n\t\t:param\tbool\t$constrain_by_prefix: TRUE to match table names by the configured dbprefix\n\t\t:returns:\tArray of table names or FALSE on failure\n\t\t:rtype:\tarray\n\n\t\tGets a list of the tables in the current database.\n\n\t.. php:method:: table_exists($table_name)\n\n\t\t:param\tstring\t$table_name: The table name\n\t\t:returns:\tTRUE if that table exists, FALSE if not\n\t\t:rtype:\tbool\n\n\t\tDetermine if a particular table exists.\n\n\t.. php:method:: list_fields($table)\n\n\t\t:param\tstring\t$table: The table name\n\t\t:returns:\tArray of field names or FALSE on failure\n\t\t:rtype:\tarray\n\n\t\tGets a list of the field names in a table.\n\n\t.. php:method:: field_exists($field_name, $table_name)\n\n\t\t:param\tstring\t$table_name: The table name\n\t\t:param\tstring\t$field_name: The field name\n\t\t:returns:\tTRUE if that field exists in that table, FALSE if not\n\t\t:rtype:\tbool\n\n\t\tDetermine if a particular field exists.\n\n\t.. php:method:: field_data($table)\n\n\t\t:param\tstring\t$table: The table name\n\t\t:returns:\tArray of field data items or FALSE on failure\n\t\t:rtype:\tarray\n\n\t\tGets a list containing field data about a table.\n\n\t.. php:method:: escape_identifiers($item, $split = TRUE)\n\n\t\t:param\tmixed\t$item: The item or array of items to escape\n\t\t:param\tbool\t$split: Whether to split identifiers when a dot is encountered\n\t\t:returns:\tThe input item(s), escaped\n\t\t:rtype:\tmixed\n\n\t\tEscape SQL identifiers, such as column, table and names.\n\n\t.. php:method:: insert_string($table, $data)\n\n\t\t:param\tstring\t$table: The target table\n\t\t:param\tarray\t$data: An associative array of key/value pairs\n\t\t:returns:\tThe SQL INSERT statement, as a string\n\t\t:rtype:\tstring\n\n\t\tGenerate an INSERT statement string.\n\n\t.. php:method:: update_string($table, $data, $where)\n\n\t\t:param\tstring\t$table: The target table\n\t\t:param\tarray\t$data: An associative array of key/value pairs\n\t\t:param\tmixed\t$where: The WHERE statement conditions\n\t\t:returns:\tThe SQL UPDATE statement, as a string\n\t\t:rtype:\tstring\n\n\t\tGenerate an UPDATE statement string.\n\n\t.. php:method:: call_function($function)\n\n\t\t:param\tstring\t$function: Function name\n\t\t:returns:\tThe function result\n\t\t:rtype:\tstring\n\n\t\tRuns a native PHP function , using a platform agnostic\n\t\twrapper.\n\n\t.. php:method:: cache_set_path([$path = ''])\n\n\t\t:param\tstring\t$path: Path to the cache directory\n\t\t:rtype:\tvoid\n\n\t\tSets the directory path to use for caching storage.\n\n\t.. php:method:: cache_on()\n\n\t\t:returns:\tTRUE if caching is on, FALSE if not\n\t\t:rtype:\tbool\n\n\t\tEnable database results caching.\n\n\t.. php:method:: cache_off()\n\n\t\t:returns:\tTRUE if caching is on, FALSE if not\n\t\t:rtype:\tbool\n\n\t\tDisable database results caching.\n\n\t.. php:method:: cache_delete([$segment_one = ''[, $segment_two = '']])\n\n\t\t:param\tstring\t$segment_one: First URI segment\n\t\t:param\tstring\t$segment_two: Second URI segment\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tDelete the cache files associated with a particular URI.\n\n\t.. php:method:: cache_delete_all()\n\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tDelete all cache files.\n\n\t.. php:method:: close()\n\n\t\t:rtype:\tvoid\n\n\t\tClose the DB Connection.\n\n\t.. php:method:: display_error([$error = ''[, $swap = ''[, $native = FALSE]]])\n\n\t\t:param\tstring\t$error: The error message\n\t\t:param\tstring\t$swap: Any \"swap\" values\n\t\t:param\tbool\t$native: Whether to localize the message\n\t\t:rtype:\tvoid\n\n\t\t:returns:\tDisplays the DB error screensends the application/views/errors/error_db.php template\n                :rtype:\tstring\n\n\t\tDisplay an error message and stop script execution.\n\n\t\tThe message is displayed using the\n\t\t*application/views/errors/error_db.php* template.\n\n\t.. php:method:: protect_identifiers($item[, $prefix_single = FALSE[, $protect_identifiers = NULL[, $field_exists = TRUE]]])\n\n\t\t:param\tstring\t$item: The item to work with\n\t\t:param\tbool\t$prefix_single: Whether to apply the dbprefix even if the input item is a single identifier\n\t\t:param\tbool\t$protect_identifiers: Whether to quote identifiers\n\t\t:param\tbool\t$field_exists: Whether the supplied item contains a field name or not\n\t\t:returns:\tThe modified item\n\t\t:rtype:\tstring\n\n\t\tTakes a column or table name (optionally with an alias)\n\t\tand applies the configured *dbprefix* to it.\n\n\t\tSome logic is necessary in order to deal with\n\t\tcolumn names that include the path. \n\n\t\tConsider a query like this::\n\n\t\t\tSELECT * FROM hostname.database.table.column AS c FROM hostname.database.table\n\n\t\tOr a query with aliasing::\n\n\t\t\tSELECT m.member_id, m.member_name FROM members AS m\n\n\t\tSince the column name can include up to four segments\n\t\t(host, DB, table, column) or also have an alias prefix,\n\t\twe need to do a bit of work to figure this out and\n\t\tinsert the table prefix (if it exists) in the proper\n\t\tposition, and escape only the correct identifiers.\n\n\t\tThis method is used extensively by the Query Builder class.\n"
  },
  {
    "path": "user_guide_src/source/database/examples.rst",
    "content": "##################################\nDatabase Quick Start: Example Code\n##################################\n\nThe following page contains example code showing how the database class\nis used. For complete details please read the individual pages\ndescribing each function.\n\nInitializing the Database Class\n===============================\n\nThe following code loads and initializes the database class based on\nyour :doc:`configuration <configuration>` settings::\n\n\t$this->load->database();\n\nOnce loaded the class is ready to be used as described below.\n\nNote: If all your pages require database access you can connect\nautomatically. See the :doc:`connecting <connecting>` page for details.\n\nStandard Query With Multiple Results (Object Version)\n=====================================================\n\n::\n\n\t$query = $this->db->query('SELECT name, title, email FROM my_table');\n\t\n\tforeach ($query->result() as $row)\n\t{\n\t\techo $row->title;\n\t\techo $row->name;\n\t\techo $row->email;\n\t}\n\t\n\techo 'Total Results: ' . $query->num_rows();\n\nThe above result() function returns an array of **objects**. Example:\n$row->title\n\nStandard Query With Multiple Results (Array Version)\n====================================================\n\n::\n\n\t$query = $this->db->query('SELECT name, title, email FROM my_table');\n\t\n\tforeach ($query->result_array() as $row)\n\t{\n\t\techo $row['title'];\n\t\techo $row['name'];\n\t\techo $row['email'];\n\t}\n\nThe above result_array() function returns an array of standard array\nindexes. Example: $row['title']\n\nStandard Query With Single Result\n=================================\n\n::\n\n\t$query = $this->db->query('SELECT name FROM my_table LIMIT 1'); \n\t$row = $query->row();\n\techo $row->name;\n\nThe above row() function returns an **object**. Example: $row->name\n\nStandard Query With Single Result (Array version)\n=================================================\n\n::\n\n\t$query = $this->db->query('SELECT name FROM my_table LIMIT 1');\n\t$row = $query->row_array();\n\techo $row['name'];\n\nThe above row_array() function returns an **array**. Example:\n$row['name']\n\nStandard Insert\n===============\n\n::\n\n\t$sql = \"INSERT INTO mytable (title, name) VALUES (\".$this->db->escape($title).\", \".$this->db->escape($name).\")\";\n\t$this->db->query($sql);\n\techo $this->db->affected_rows();\n\nQuery Builder Query\n===================\n\nThe :doc:`Query Builder Pattern <query_builder>` gives you a simplified\nmeans of retrieving data::\n\n\t$query = $this->db->get('table_name');\n\t\n\tforeach ($query->result() as $row)\n\t{\n\t\techo $row->title;\n\t}\n\nThe above get() function retrieves all the results from the supplied\ntable. The :doc:`Query Builder <query_builder>` class contains a full\ncompliment of functions for working with data.\n\nQuery Builder Insert\n====================\n\n::\n\n\t$data = array(\n\t\t'title' => $title,\n\t\t'name' => $name,\n\t\t'date' => $date\n\t);\n\t\n\t$this->db->insert('mytable', $data);  // Produces: INSERT INTO mytable (title, name, date) VALUES ('{$title}', '{$name}', '{$date}')\n\n"
  },
  {
    "path": "user_guide_src/source/database/forge.rst",
    "content": "####################\nDatabase Forge Class\n####################\n\nThe Database Forge Class contains methods that help you manage your\ndatabase.\n\n.. contents:: Table of Contents\n    :depth: 3\n\n****************************\nInitializing the Forge Class\n****************************\n\n.. important:: In order to initialize the Forge class, your database\n\tdriver must already be running, since the forge class relies on it.\n\nLoad the Forge Class as follows::\n\n\t$this->load->dbforge()\n\nYou can also pass another database object to the DB Forge loader, in case\nthe database you want to manage isn't the default one::\n\n\t$this->myforge = $this->load->dbforge($this->other_db, TRUE);\n\nIn the above example, we're passing a custom database object as the first\nparameter and then tell it to return the dbforge object, instead of\nassigning it directly to ``$this->dbforge``.\n\n.. note:: Both of the parameters can be used individually, just pass an empty\n\tvalue as the first one if you wish to skip it.\n\nOnce initialized you will access the methods using the ``$this->dbforge``\nobject::\n\n\t$this->dbforge->some_method();\n\n*******************************\nCreating and Dropping Databases\n*******************************\n\n**$this->dbforge->create_database('db_name')**\n\nPermits you to create the database specified in the first parameter.\nReturns TRUE/FALSE based on success or failure::\n\n\tif ($this->dbforge->create_database('my_db'))\n\t{\n\t\techo 'Database created!';\n\t}\n\n**$this->dbforge->drop_database('db_name')**\n\nPermits you to drop the database specified in the first parameter.\nReturns TRUE/FALSE based on success or failure::\n\n\tif ($this->dbforge->drop_database('my_db'))\n\t{\n\t\techo 'Database deleted!';\n\t}\n\n\n****************************\nCreating and Dropping Tables\n****************************\n\nThere are several things you may wish to do when creating tables. Add\nfields, add keys to the table, alter columns. CodeIgniter provides a\nmechanism for this.\n\nAdding fields\n=============\n\nFields are created via an associative array. Within the array you must\ninclude a 'type' key that relates to the datatype of the field. For\nexample, INT, VARCHAR, TEXT, etc. Many datatypes (for example VARCHAR)\nalso require a 'constraint' key.\n\n::\n\n\t$fields = array(\n\t\t'users' => array(\n\t\t\t'type' => 'VARCHAR',\n\t\t\t'constraint' => '100',\n\t\t),\n\t);\n\t// will translate to \"users VARCHAR(100)\" when the field is added.\n\n\nAdditionally, the following key/values can be used:\n\n-  unsigned/true : to generate \"UNSIGNED\" in the field definition.\n-  default/value : to generate a default value in the field definition.\n-  null/true : to generate \"NULL\" in the field definition. Without this,\n   the field will default to \"NOT NULL\".\n-  auto_increment/true : generates an auto_increment flag on the\n   field. Note that the field type must be a type that supports this,\n   such as integer.\n-  unique/true : to generate a unique key for the field definition.\n\n::\n\n\t$fields = array(\n\t\t'blog_id' => array(\n\t\t\t'type' => 'INT',\n\t\t\t'constraint' => 5,\n\t\t\t'unsigned' => TRUE,\n\t\t\t'auto_increment' => TRUE\n\t\t),\n\t\t'blog_title' => array(\n\t\t\t'type' => 'VARCHAR',\n\t\t\t'constraint' => '100',\n\t\t\t'unique' => TRUE,\n\t\t),\n\t\t'blog_author' => array(\n\t\t\t'type' =>'VARCHAR',\n\t\t\t'constraint' => '100',\n\t\t\t'default' => 'King of Town',\n\t\t),\n\t\t'blog_description' => array(\n\t\t\t'type' => 'TEXT',\n\t\t\t'null' => TRUE,\n\t\t),\n\t);\n\n\nAfter the fields have been defined, they can be added using\n``$this->dbforge->add_field($fields);`` followed by a call to the\n``create_table()`` method.\n\n**$this->dbforge->add_field()**\n\nThe add fields method will accept the above array.\n\n\nPassing strings as fields\n-------------------------\n\nIf you know exactly how you want a field to be created, you can pass the\nstring into the field definitions with add_field()\n\n::\n\n\t$this->dbforge->add_field(\"label varchar(100) NOT NULL DEFAULT 'default label'\");\n\n\n.. note:: Passing raw strings as fields cannot be followed by ``add_key()`` calls on those fields.\n\n.. note:: Multiple calls to add_field() are cumulative.\n\nCreating an id field\n--------------------\n\nThere is a special exception for creating id fields. A field with type\nid will automatically be assigned as an INT(9) auto_incrementing\nPrimary Key.\n\n::\n\n\t$this->dbforge->add_field('id');\n\t// gives id INT(9) NOT NULL AUTO_INCREMENT\n\n\nAdding Keys\n===========\n\nGenerally speaking, you'll want your table to have Keys. This is\naccomplished with $this->dbforge->add_key('field'). An optional second\nparameter set to TRUE will make it a primary key. Note that add_key()\nmust be followed by a call to create_table().\n\nMultiple column non-primary keys must be sent as an array. Sample output\nbelow is for MySQL.\n\n::\n\n\t$this->dbforge->add_key('blog_id', TRUE);\n\t// gives PRIMARY KEY `blog_id` (`blog_id`)\n\n\t$this->dbforge->add_key('blog_id', TRUE);\n\t$this->dbforge->add_key('site_id', TRUE);\n\t// gives PRIMARY KEY `blog_id_site_id` (`blog_id`, `site_id`)\n\n\t$this->dbforge->add_key('blog_name');\n\t// gives KEY `blog_name` (`blog_name`)\n\n\t$this->dbforge->add_key(array('blog_name', 'blog_label'));\n\t// gives KEY `blog_name_blog_label` (`blog_name`, `blog_label`)\n\n\nCreating a table\n================\n\nAfter fields and keys have been declared, you can create a new table\nwith\n\n::\n\n\t$this->dbforge->create_table('table_name');\n\t// gives CREATE TABLE table_name\n\n\nAn optional second parameter set to TRUE adds an \"IF NOT EXISTS\" clause\ninto the definition\n\n::\n\n\t$this->dbforge->create_table('table_name', TRUE);\n\t// gives CREATE TABLE IF NOT EXISTS table_name\n\nYou could also pass optional table attributes, such as MySQL's ``ENGINE``::\n\n\t$attributes = array('ENGINE' => 'InnoDB');\n\t$this->dbforge->create_table('table_name', FALSE, $attributes);\n\t// produces: CREATE TABLE `table_name` (...) ENGINE = InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci\n\n.. note:: Unless you specify the ``CHARACTER SET`` and/or ``COLLATE`` attributes,\n\t``create_table()`` will always add them with your configured *char_set*\n\tand *dbcollat* values, as long as they are not empty (MySQL only).\n\n\nDropping a table\n================\n\nExecute a DROP TABLE statement and optionally add an IF EXISTS clause.\n\n::\n\n\t// Produces: DROP TABLE table_name\n\t$this->dbforge->drop_table('table_name');\n\n\t// Produces: DROP TABLE IF EXISTS table_name\n\t$this->dbforge->drop_table('table_name',TRUE);\n\n\nRenaming a table\n================\n\nExecutes a TABLE rename\n\n::\n\n\t$this->dbforge->rename_table('old_table_name', 'new_table_name');\n\t// gives ALTER TABLE old_table_name RENAME TO new_table_name\n\n\n****************\nModifying Tables\n****************\n\nAdding a Column to a Table\n==========================\n\n**$this->dbforge->add_column()**\n\nThe ``add_column()`` method is used to modify an existing table. It\naccepts the same field array as above, and can be used for an unlimited\nnumber of additional fields.\n\n::\n\n\t$fields = array(\n\t\t'preferences' => array('type' => 'TEXT')\n\t);\n\t$this->dbforge->add_column('table_name', $fields);\n\t// Executes: ALTER TABLE table_name ADD preferences TEXT\n\nIf you are using MySQL or CUBIRD, then you can take advantage of their\nAFTER and FIRST clauses to position the new column.\n\nExamples::\n\n\t// Will place the new column after the `another_field` column:\n\t$fields = array(\n\t\t'preferences' => array('type' => 'TEXT', 'after' => 'another_field')\n\t);\n\n\t// Will place the new column at the start of the table definition:\n\t$fields = array(\n\t\t'preferences' => array('type' => 'TEXT', 'first' => TRUE)\n\t);\n\n\nDropping a Column From a Table\n==============================\n\n**$this->dbforge->drop_column()**\n\nUsed to remove a column from a table.\n\n::\n\n\t$this->dbforge->drop_column('table_name', 'column_to_drop');\n\n\n\nModifying a Column in a Table\n=============================\n\n**$this->dbforge->modify_column()**\n\nThe usage of this method is identical to ``add_column()``, except it\nalters an existing column rather than adding a new one. In order to\nchange the name you can add a \"name\" key into the field defining array.\n\n::\n\n\t$fields = array(\n\t\t'old_name' => array(\n\t\t\t'name' => 'new_name',\n\t\t\t'type' => 'TEXT',\n\t\t),\n\t);\n\t$this->dbforge->modify_column('table_name', $fields);\n\t// gives ALTER TABLE table_name CHANGE old_name new_name TEXT\n\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_DB_forge\n\n\t.. php:method:: add_column($table[, $field = array()])\n\n\t\t:param\tstring\t$table: Table name to add the column to\n\t\t:param\tarray\t$field: Column definition(s)\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tAdds a column to a table. Usage:  See `Adding a Column to a Table`_.\n\n\t.. php:method:: add_field($field)\n\n\t\t:param\tarray\t$field: Field definition to add\n\t\t:returns:\tCI_DB_forge instance (method chaining)\n\t\t:rtype:\tCI_DB_forge\n\n                Adds a field to the set that will be used to create a table. Usage:  See `Adding fields`_.\n\n\t.. php:method:: add_key($key[, $primary = FALSE])\n\n\t\t:param\tarray\t$key: Name of a key field\n\t\t:param\tbool\t$primary: Set to TRUE if it should be a primary key or a regular one\n\t\t:returns:\tCI_DB_forge instance (method chaining)\n\t\t:rtype:\tCI_DB_forge\n\n\t\tAdds a key to the set that will be used to create a table. Usage:  See `Adding Keys`_.\n\n\t.. php:method:: create_database($db_name)\n\n\t\t:param\tstring\t$db_name: Name of the database to create\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tCreates a new database. Usage:  See `Creating and Dropping Databases`_.\n\n\t.. php:method:: create_table($table[, $if_not_exists = FALSE[, array $attributes = array()]])\n\n\t\t:param\tstring\t$table: Name of the table to create\n\t\t:param\tstring\t$if_not_exists: Set to TRUE to add an 'IF NOT EXISTS' clause\n\t\t:param\tstring\t$attributes: An associative array of table attributes\n\t\t:returns:  TRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tCreates a new table. Usage:  See `Creating a table`_.\n\n\t.. php:method:: drop_column($table, $column_name)\n\n\t\t:param\tstring\t$table: Table name\n\t\t:param\tstring\t$column_name: The column name to drop\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tDrops a column from a table. Usage:  See `Dropping a Column From a Table`_.\n\n\t.. php:method:: drop_database($db_name)\n\n\t\t:param\tstring\t$db_name: Name of the database to drop\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tDrops a database. Usage:  See `Creating and Dropping Databases`_.\n\n\t.. php:method:: drop_table($table_name[, $if_exists = FALSE])\n\n\t\t:param\tstring\t$table: Name of the table to drop\n\t\t:param\tstring\t$if_exists: Set to TRUE to add an 'IF EXISTS' clause\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tDrops a table. Usage:  See `Dropping a table`_.\n\n\t.. php:method:: modify_column($table, $field)\n\n\t\t:param\tstring\t$table: Table name\n\t\t:param\tarray\t$field: Column definition(s)\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tModifies a table column. Usage:  See `Modifying a Column in a Table`_.\n\n\t.. php:method:: rename_table($table_name, $new_table_name)\n\n\t\t:param\tstring\t$table: Current of the table\n\t\t:param\tstring\t$new_table_name: New name of the table\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tRenames a table. Usage:  See `Renaming a table`_.\n"
  },
  {
    "path": "user_guide_src/source/database/helpers.rst",
    "content": "####################\nQuery Helper Methods\n####################\n\nInformation From Executing a Query\n==================================\n\n**$this->db->insert_id()**\n\nThe insert ID number when performing database inserts.\n\n.. note:: If using the PDO driver with PostgreSQL, or using the Interbase\n\tdriver, this function requires a $name parameter, which specifies the \n\tappropriate sequence to check for the insert id.\n\n**$this->db->affected_rows()**\n\nDisplays the number of affected rows, when doing \"write\" type queries\n(insert, update, etc.).\n\n.. note:: In MySQL \"DELETE FROM TABLE\" returns 0 affected rows. The database\n\tclass has a small hack that allows it to return the correct number of\n\taffected rows. By default this hack is enabled but it can be turned off\n\tin the database driver file.\n\n**$this->db->last_query()**\n\nReturns the last query that was run (the query string, not the result).\nExample::\n\n\t$str = $this->db->last_query();\n\t\n\t// Produces:  SELECT * FROM sometable....\n\n\n.. note:: Disabling the **save_queries** setting in your database\n\tconfiguration will render this function useless.\n\nInformation About Your Database\n===============================\n\n**$this->db->count_all()**\n\nPermits you to determine the number of rows in a particular table.\nSubmit the table name in the first parameter. Example::\n\n\techo $this->db->count_all('my_table');\n\t\n\t// Produces an integer, like 25\n\n**$this->db->platform()**\n\nOutputs the database platform you are running (MySQL, MS SQL, Postgres,\netc...)::\n\n\techo $this->db->platform();\n\n**$this->db->version()**\n\nOutputs the database version you are running::\n\n\techo $this->db->version();\n\nMaking Your Queries Easier\n==========================\n\n**$this->db->insert_string()**\n\nThis function simplifies the process of writing database inserts. It\nreturns a correctly formatted SQL insert string. Example::\n\n\t$data = array('name' => $name, 'email' => $email, 'url' => $url);\n\t\n\t$str = $this->db->insert_string('table_name', $data);\n\nThe first parameter is the table name, the second is an associative\narray with the data to be inserted. The above example produces::\n\n\tINSERT INTO table_name (name, email, url) VALUES ('Rick', 'rick@example.com', 'example.com')\n\n.. note:: Values are automatically escaped, producing safer queries.\n\n**$this->db->update_string()**\n\nThis function simplifies the process of writing database updates. It\nreturns a correctly formatted SQL update string. Example::\n\n\t$data = array('name' => $name, 'email' => $email, 'url' => $url);\n\t\n\t$where = \"author_id = 1 AND status = 'active'\";\n\t\n\t$str = $this->db->update_string('table_name', $data, $where);\n\nThe first parameter is the table name, the second is an associative\narray with the data to be updated, and the third parameter is the\n\"where\" clause. The above example produces::\n\n\t UPDATE table_name SET name = 'Rick', email = 'rick@example.com', url = 'example.com' WHERE author_id = 1 AND status = 'active'\n\n.. note:: Values are automatically escaped, producing safer queries."
  },
  {
    "path": "user_guide_src/source/database/index.rst",
    "content": "##################\nDatabase Reference\n##################\n\nCodeIgniter comes with a full-featured and very fast abstracted database\nclass that supports both traditional structures and Query Builder\npatterns. The database functions offer clear, simple syntax.\n\n.. toctree::\n\t:titlesonly:\n\t\n\tQuick Start: Usage Examples <examples>\n\tDatabase Configuration <configuration>\n\tConnecting to a Database <connecting>\n\tRunning Queries <queries>\n\tGenerating Query Results <results>\n\tQuery Helper Functions <helpers>\n\tQuery Builder Class <query_builder>\n\tTransactions <transactions>\n\tGetting MetaData <metadata>\n\tCustom Function Calls <call_function>\n\tQuery Caching <caching>\n\tDatabase Manipulation with Database Forge <forge>\n\tDatabase Utilities Class <utilities>\n\tDatabase Driver Reference <db_driver_reference>"
  },
  {
    "path": "user_guide_src/source/database/metadata.rst",
    "content": "#################\nDatabase Metadata\n#################\n\n**************\nTable MetaData\n**************\n\nThese functions let you fetch table information.\n\nList the Tables in Your Database\n================================\n\n**$this->db->list_tables();**\n\nReturns an array containing the names of all the tables in the database\nyou are currently connected to. Example::\n\n\t$tables = $this->db->list_tables();\n\t\n\tforeach ($tables as $table)\n\t{\n\t\techo $table;\n\t}\n\n\nDetermine If a Table Exists\n===========================\n\n**$this->db->table_exists();**\n\nSometimes it's helpful to know whether a particular table exists before\nrunning an operation on it. Returns a boolean TRUE/FALSE. Usage example::\n\n\tif ($this->db->table_exists('table_name'))\n\t{\n\t\t// some code...\n\t}\n\n.. note:: Replace *table_name* with the name of the table you are looking for.\n\n\n**************\nField MetaData\n**************\n\nList the Fields in a Table\n==========================\n\n**$this->db->list_fields()**\n\nReturns an array containing the field names. This query can be called\ntwo ways:\n\n1. You can supply the table name and call it from the $this->db->\nobject::\n\n\t$fields = $this->db->list_fields('table_name');\n\t\n\tforeach ($fields as $field)\n\t{\n\t\techo $field;\n\t}\n\n2. You can gather the field names associated with any query you run by\ncalling the function from your query result object::\n\n\t$query = $this->db->query('SELECT * FROM some_table');\n\t\n\tforeach ($query->list_fields() as $field)\n\t{\n\t\techo $field;\n\t}\n\n\nDetermine If a Field is Present in a Table\n==========================================\n\n**$this->db->field_exists()**\n\nSometimes it's helpful to know whether a particular field exists before\nperforming an action. Returns a boolean TRUE/FALSE. Usage example::\n\n\tif ($this->db->field_exists('field_name', 'table_name'))\n\t{\n\t\t// some code...\n\t}\n\n.. note:: Replace *field_name* with the name of the column you are looking\n\tfor, and replace *table_name* with the name of the table you are\n\tlooking for.\n\n\nRetrieve Field Metadata\n=======================\n\n**$this->db->field_data()**\n\nReturns an array of objects containing field information.\n\nSometimes it's helpful to gather the field names or other metadata, like\nthe column type, max length, etc.\n\n.. note:: Not all databases provide meta-data.\n\nUsage example::\n\n\t$fields = $this->db->field_data('table_name');\n\t\n\tforeach ($fields as $field)\n\t{\n\t\techo $field->name;\n\t\techo $field->type;\n\t\techo $field->max_length;\n\t\techo $field->primary_key;\n\t}\n\nIf you have run a query already you can use the result object instead of\nsupplying the table name::\n\n\t$query = $this->db->query(\"YOUR QUERY\");\n\t$fields = $query->field_data();\n\nThe following data is available from this function if supported by your\ndatabase:\n\n-  name - column name\n-  max_length - maximum length of the column\n-  primary_key - 1 if the column is a primary key\n-  type - the type of the column\n"
  },
  {
    "path": "user_guide_src/source/database/queries.rst",
    "content": "#######\nQueries\n#######\n\n************\nQuery Basics\n************\n\nRegular Queries\n===============\n\nTo submit a query, use the **query** function::\n\n\t$this->db->query('YOUR QUERY HERE');\n\nThe query() function returns a database result **object** when \"read\"\ntype queries are run, which you can use to :doc:`show your\nresults <results>`. When \"write\" type queries are run it simply\nreturns TRUE or FALSE depending on success or failure. When retrieving\ndata you will typically assign the query to your own variable, like\nthis::\n\n\t$query = $this->db->query('YOUR QUERY HERE');\n\nSimplified Queries\n==================\n\nThe **simple_query** method is a simplified version of the \n$this->db->query() method. It DOES\nNOT return a database result set, nor does it set the query timer, or\ncompile bind data, or store your query for debugging. It simply lets you\nsubmit a query. Most users will rarely use this function.\n\nIt returns whatever the database drivers' \"execute\" function returns.\nThat typically is TRUE/FALSE on success or failure for write type queries\nsuch as INSERT, DELETE or UPDATE statements (which is what it really\nshould be used for) and a resource/object on success for queries with\nfetchable results.\n\n::\n\n\tif ($this->db->simple_query('YOUR QUERY'))\n\t{\n\t\techo \"Success!\";\n\t}\n\telse\n\t{\n\t\techo \"Query failed!\";\n\t}\n\n.. note:: PostgreSQL's ``pg_exec()`` function (for example) always\n\treturns a resource on success, even for write type queries.\n\tSo take that in mind if you're looking for a boolean value.\n\n***************************************\nWorking with Database prefixes manually\n***************************************\n\nIf you have configured a database prefix and would like to prepend it to\na table name for use in a native SQL query for example, then you can use\nthe following::\n\n\t$this->db->dbprefix('tablename'); // outputs prefix_tablename\n\n\nIf for any reason you would like to change the prefix programatically\nwithout needing to create a new connection, you can use this method::\n\n\t$this->db->set_dbprefix('newprefix_');\n\t$this->db->dbprefix('tablename'); // outputs newprefix_tablename\n\n\n**********************\nProtecting identifiers\n**********************\n\nIn many databases it is advisable to protect table and field names - for\nexample with backticks in MySQL. **Query Builder queries are\nautomatically protected**, however if you need to manually protect an\nidentifier you can use::\n\n\t$this->db->protect_identifiers('table_name');\n\n.. important:: Although the Query Builder will try its best to properly\n\tquote any field and table names that you feed it, note that it\n\tis NOT designed to work with arbitrary user input. DO NOT feed it\n\twith unsanitized user data.\n\nThis function will also add a table prefix to your table, assuming you\nhave a prefix specified in your database config file. To enable the\nprefixing set TRUE (boolean) via the second parameter::\n\n\t$this->db->protect_identifiers('table_name', TRUE);\n\n\n****************\nEscaping Queries\n****************\n\nIt's a very good security practice to escape your data before submitting\nit into your database. CodeIgniter has three methods that help you do\nthis:\n\n#. **$this->db->escape()** This function determines the data type so\n   that it can escape only string data. It also automatically adds\n   single quotes around the data so you don't have to:\n   ::\n\n\t$sql = \"INSERT INTO table (title) VALUES(\".$this->db->escape($title).\")\";\n\n#. **$this->db->escape_str()** This function escapes the data passed to\n   it, regardless of type. Most of the time you'll use the above\n   function rather than this one. Use the function like this:\n   ::\n\n\t$sql = \"INSERT INTO table (title) VALUES('\".$this->db->escape_str($title).\"')\";\n\n#. **$this->db->escape_like_str()** This method should be used when\n   strings are to be used in LIKE conditions so that LIKE wildcards\n   ('%', '\\_') in the string are also properly escaped.\n\n::\n\n        $search = '20% raise'; \n        $sql = \"SELECT id FROM table WHERE column LIKE '%\" .\n            $this->db->escape_like_str($search).\"%' ESCAPE '!'\";\n\n.. important:: The ``escape_like_str()`` method uses '!' (exclamation mark)\n\tto escape special characters for *LIKE* conditions. Because this\n\tmethod escapes partial strings that you would wrap in quotes\n\tyourself, it cannot automatically add the ``ESCAPE '!'``\n\tcondition for you, and so you'll have to manually do that.\n\n\n**************\nQuery Bindings\n**************\n\nBindings enable you to simplify your query syntax by letting the system\nput the queries together for you. Consider the following example::\n\n\t$sql = \"SELECT * FROM some_table WHERE id = ? AND status = ? AND author = ?\";\n\t$this->db->query($sql, array(3, 'live', 'Rick'));\n\nThe question marks in the query are automatically replaced with the\nvalues in the array in the second parameter of the query function.\n\nBinding also work with arrays, which will be transformed to IN sets::\n\n\t$sql = \"SELECT * FROM some_table WHERE id IN ? AND status = ? AND author = ?\";\n\t$this->db->query($sql, array(array(3, 6), 'live', 'Rick'));\n\nThe resulting query will be::\n\n\tSELECT * FROM some_table WHERE id IN (3,6) AND status = 'live' AND author = 'Rick'\n\nThe secondary benefit of using binds is that the values are\nautomatically escaped, producing safer queries. You don't have to\nremember to manually escape data; the engine does it automatically for\nyou.\n\n***************\nHandling Errors\n***************\n\n**$this->db->error();**\n\nIf you need to get the last error that has occurred, the error() method\nwill return an array containing its code and message. Here's a quick\nexample::\n\n\tif ( ! $this->db->simple_query('SELECT `example_field` FROM `example_table`'))\n\t{\n\t\t$error = $this->db->error(); // Has keys 'code' and 'message'\n\t}\n\n"
  },
  {
    "path": "user_guide_src/source/database/query_builder.rst",
    "content": "###################\nQuery Builder Class\n###################\n\nCodeIgniter gives you access to a Query Builder class. This pattern\nallows information to be retrieved, inserted, and updated in your\ndatabase with minimal scripting. In some cases only one or two lines\nof code are necessary to perform a database action.\nCodeIgniter does not require that each database table be its own class\nfile. It instead provides a more simplified interface.\n\nBeyond simplicity, a major benefit to using the Query Builder features\nis that it allows you to create database independent applications, since\nthe query syntax is generated by each database adapter. It also allows\nfor safer queries, since the values are escaped automatically by the\nsystem.\n\n.. note:: If you intend to write your own queries you can disable this\n\tclass in your database config file, allowing the core database library\n\tand adapter to utilize fewer resources.\n\n.. contents::\n    :local:\n    :depth: 1\n\n**************\nSelecting Data\n**************\n\nThe following functions allow you to build SQL **SELECT** statements.\n\n**$this->db->get()**\n\nRuns the selection query and returns the result. Can be used by itself\nto retrieve all records from a table::\n\n\t$query = $this->db->get('mytable');  // Produces: SELECT * FROM mytable\n\nThe second and third parameters enable you to set a limit and offset\nclause::\n\n\t$query = $this->db->get('mytable', 10, 20);\n\n\t// Executes: SELECT * FROM mytable LIMIT 20, 10\n\t// (in MySQL. Other databases have slightly different syntax)\n\nYou'll notice that the above function is assigned to a variable named\n$query, which can be used to show the results::\n\n\t$query = $this->db->get('mytable');\n\n\tforeach ($query->result() as $row)\n\t{\n\t\techo $row->title;\n\t}\n\nPlease visit the :doc:`result functions <results>` page for a full\ndiscussion regarding result generation.\n\n**$this->db->get_compiled_select()**\n\nCompiles the selection query just like **$this->db->get()** but does not *run*\nthe query. This method simply returns the SQL query as a string.\n\nExample::\n\n\t$sql = $this->db->get_compiled_select('mytable');\n\techo $sql;\n\n\t// Prints string: SELECT * FROM mytable\n\nThe second parameter enables you to set whether or not the query builder query\nwill be reset (by default it will be reset, just like when using `$this->db->get()`)::\n\n\techo $this->db->limit(10,20)->get_compiled_select('mytable', FALSE);\n\n\t// Prints string: SELECT * FROM mytable LIMIT 20, 10\n\t// (in MySQL. Other databases have slightly different syntax)\n\n\techo $this->db->select('title, content, date')->get_compiled_select();\n\n\t// Prints string: SELECT title, content, date FROM mytable LIMIT 20, 10\n\nThe key thing to notice in the above example is that the second query did not\nutilize **$this->db->from()** and did not pass a table name into the first\nparameter. The reason for this outcome is because the query has not been\nexecuted using **$this->db->get()** which resets values or reset directly\nusing **$this->db->reset_query()**.\n\n**$this->db->get_where()**\n\nIdentical to the above function except that it permits you to add a\n\"where\" clause in the second parameter, instead of using the db->where()\nfunction::\n\n\t$query = $this->db->get_where('mytable', array('id' => $id), $limit, $offset);\n\nPlease read the about the where function below for more information.\n\n.. note:: get_where() was formerly known as getwhere(), which has been removed\n\n**$this->db->select()**\n\nPermits you to write the SELECT portion of your query::\n\n\t$this->db->select('title, content, date');\n\t$query = $this->db->get('mytable');\n\n\t// Executes: SELECT title, content, date FROM mytable\n\n.. note:: If you are selecting all (\\*) from a table you do not need to\n\tuse this function. When omitted, CodeIgniter assumes that you wish\n\tto select all fields and automatically adds 'SELECT \\*'.\n\n``$this->db->select()`` accepts an optional second parameter. If you set it\nto FALSE, CodeIgniter will not try to protect your field or table names.\nThis is useful if you need a compound select statement where automatic\nescaping of fields may break them.\n\n::\n\n\t$this->db->select('(SELECT SUM(payments.amount) FROM payments WHERE payments.invoice_id=4) AS amount_paid', FALSE);\n\t$query = $this->db->get('mytable');\n\n**$this->db->select_max()**\n\nWrites a ``SELECT MAX(field)`` portion for your query. You can optionally\ninclude a second parameter to rename the resulting field.\n\n::\n\n\t$this->db->select_max('age');\n\t$query = $this->db->get('members');  // Produces: SELECT MAX(age) as age FROM members\n\n\t$this->db->select_max('age', 'member_age');\n\t$query = $this->db->get('members'); // Produces: SELECT MAX(age) as member_age FROM members\n\n\n**$this->db->select_min()**\n\nWrites a \"SELECT MIN(field)\" portion for your query. As with\nselect_max(), You can optionally include a second parameter to rename\nthe resulting field.\n\n::\n\n\t$this->db->select_min('age');\n\t$query = $this->db->get('members'); // Produces: SELECT MIN(age) as age FROM members\n\n\n**$this->db->select_avg()**\n\nWrites a \"SELECT AVG(field)\" portion for your query. As with\nselect_max(), You can optionally include a second parameter to rename\nthe resulting field.\n\n::\n\n\t$this->db->select_avg('age');\n\t$query = $this->db->get('members'); // Produces: SELECT AVG(age) as age FROM members\n\n\n**$this->db->select_sum()**\n\nWrites a \"SELECT SUM(field)\" portion for your query. As with\nselect_max(), You can optionally include a second parameter to rename\nthe resulting field.\n\n::\n\n\t$this->db->select_sum('age');\n\t$query = $this->db->get('members'); // Produces: SELECT SUM(age) as age FROM members\n\n**$this->db->from()**\n\nPermits you to write the FROM portion of your query::\n\n\t$this->db->select('title, content, date');\n\t$this->db->from('mytable');\n\t$query = $this->db->get();  // Produces: SELECT title, content, date FROM mytable\n\n.. note:: As shown earlier, the FROM portion of your query can be specified\n\tin the $this->db->get() function, so use whichever method you prefer.\n\n**$this->db->join()**\n\nPermits you to write the JOIN portion of your query::\n\n\t$this->db->select('*');\n\t$this->db->from('blogs');\n\t$this->db->join('comments', 'comments.id = blogs.id');\n\t$query = $this->db->get();\n\n\t// Produces:\n\t// SELECT * FROM blogs JOIN comments ON comments.id = blogs.id\n\nMultiple function calls can be made if you need several joins in one\nquery.\n\nIf you need a specific type of JOIN you can specify it via the third\nparameter of the function. Options are: left, right, outer, inner, left\nouter, right outer and full outer.\n\n::\n\n\t$this->db->join('comments', 'comments.id = blogs.id', 'left');\n\t// Produces: LEFT JOIN comments ON comments.id = blogs.id\n\n*************************\nLooking for Specific Data\n*************************\n\n**$this->db->where()**\n\nThis function enables you to set **WHERE** clauses using one of four\nmethods:\n\n.. note:: All values passed to this function are escaped automatically,\n\tproducing safer queries.\n\n#. **Simple key/value method:**\n\n\t::\n\n\t\t$this->db->where('name', $name); // Produces: WHERE name = 'Joe'\n\n\tNotice that the equal sign is added for you.\n\n\tIf you use multiple function calls they will be chained together with\n\tAND between them:\n\n\t::\n\n\t\t$this->db->where('name', $name);\n\t\t$this->db->where('title', $title);\n\t\t$this->db->where('status', $status);\n\t\t// WHERE name = 'Joe' AND title = 'boss' AND status = 'active'\n\n#. **Custom key/value method:**\n\n\tYou can include an operator in the first parameter in order to\n\tcontrol the comparison:\n\n\t::\n\n\t\t$this->db->where('name !=', $name);\n\t\t$this->db->where('id <', $id); // Produces: WHERE name != 'Joe' AND id < 45\n\n#. **Associative array method:**\n\n\t::\n\n\t\t$array = array('name' => $name, 'title' => $title, 'status' => $status);\n\t\t$this->db->where($array);\n\t\t// Produces: WHERE name = 'Joe' AND title = 'boss' AND status = 'active'\n\n\tYou can include your own operators using this method as well:\n\n\t::\n\n\t\t$array = array('name !=' => $name, 'id <' => $id, 'date >' => $date);\n\t\t$this->db->where($array);\n\n#. **Custom string:**\n\tYou can write your own clauses manually::\n\n\t\t$where = \"name='Joe' AND status='boss' OR status='active'\";\n\t\t$this->db->where($where);\n\n\n``$this->db->where()`` accepts an optional third parameter. If you set it to\nFALSE, CodeIgniter will not try to protect your field or table names.\n\n::\n\n\t$this->db->where('MATCH (field) AGAINST (\"value\")', NULL, FALSE);\n\n**$this->db->or_where()**\n\nThis function is identical to the one above, except that multiple\ninstances are joined by OR::\n\n\t$this->db->where('name !=', $name);\n\t$this->db->or_where('id >', $id);  // Produces: WHERE name != 'Joe' OR id > 50\n\n.. note:: or_where() was formerly known as orwhere(), which has been\n\tremoved.\n\n**$this->db->where_in()**\n\nGenerates a WHERE field IN ('item', 'item') SQL query joined with AND if\nappropriate\n\n::\n\n\t$names = array('Frank', 'Todd', 'James');\n\t$this->db->where_in('username', $names);\n\t// Produces: WHERE username IN ('Frank', 'Todd', 'James')\n\n\n**$this->db->or_where_in()**\n\nGenerates a WHERE field IN ('item', 'item') SQL query joined with OR if\nappropriate\n\n::\n\n\t$names = array('Frank', 'Todd', 'James');\n\t$this->db->or_where_in('username', $names);\n\t// Produces: OR username IN ('Frank', 'Todd', 'James')\n\n**$this->db->where_not_in()**\n\nGenerates a WHERE field NOT IN ('item', 'item') SQL query joined with\nAND if appropriate\n\n::\n\n\t$names = array('Frank', 'Todd', 'James');\n\t$this->db->where_not_in('username', $names);\n\t// Produces: WHERE username NOT IN ('Frank', 'Todd', 'James')\n\n\n**$this->db->or_where_not_in()**\n\nGenerates a WHERE field NOT IN ('item', 'item') SQL query joined with OR\nif appropriate\n\n::\n\n\t$names = array('Frank', 'Todd', 'James');\n\t$this->db->or_where_not_in('username', $names);\n\t// Produces: OR username NOT IN ('Frank', 'Todd', 'James')\n\n************************\nLooking for Similar Data\n************************\n\n**$this->db->like()**\n\nThis method enables you to generate **LIKE** clauses, useful for doing\nsearches.\n\n.. note:: All values passed to this method are escaped automatically.\n\n#. **Simple key/value method:**\n\n\t::\n\n\t\t$this->db->like('title', 'match');\n\t\t// Produces: WHERE `title` LIKE '%match%' ESCAPE '!'\n\n\tIf you use multiple method calls they will be chained together with\n\tAND between them::\n\n\t\t$this->db->like('title', 'match');\n\t\t$this->db->like('body', 'match');\n\t\t// WHERE `title` LIKE '%match%' ESCAPE '!' AND  `body` LIKE '%match% ESCAPE '!'\n\n\tIf you want to control where the wildcard (%) is placed, you can use\n\tan optional third argument. Your options are 'before', 'after', 'none' and\n\t'both' (which is the default).\n\n\t::\n\n\t\t$this->db->like('title', 'match', 'before');\t// Produces: WHERE `title` LIKE '%match' ESCAPE '!'\n\t\t$this->db->like('title', 'match', 'after');\t// Produces: WHERE `title` LIKE 'match%' ESCAPE '!'\n\t\t$this->db->like('title', 'match', 'none');\t// Produces: WHERE `title` LIKE 'match' ESCAPE '!'\n\t\t$this->db->like('title', 'match', 'both');\t// Produces: WHERE `title` LIKE '%match%' ESCAPE '!'\n\n#. **Associative array method:**\n\n\t::\n\n\t\t$array = array('title' => $match, 'page1' => $match, 'page2' => $match);\n\t\t$this->db->like($array);\n\t\t// WHERE `title` LIKE '%match%' ESCAPE '!' AND  `page1` LIKE '%match%' ESCAPE '!' AND  `page2` LIKE '%match%' ESCAPE '!'\n\n\n**$this->db->or_like()**\n\nThis method is identical to the one above, except that multiple\ninstances are joined by OR::\n\n\t$this->db->like('title', 'match'); $this->db->or_like('body', $match);\n\t// WHERE `title` LIKE '%match%' ESCAPE '!' OR  `body` LIKE '%match%' ESCAPE '!'\n\n.. note:: ``or_like()`` was formerly known as ``orlike()``, which has been removed.\n\n**$this->db->not_like()**\n\nThis method is identical to ``like()``, except that it generates\nNOT LIKE statements::\n\n\t$this->db->not_like('title', 'match');\t// WHERE `title` NOT LIKE '%match% ESCAPE '!'\n\n**$this->db->or_not_like()**\n\nThis method is identical to ``not_like()``, except that multiple\ninstances are joined by OR::\n\n\t$this->db->like('title', 'match');\n\t$this->db->or_not_like('body', 'match');\n\t// WHERE `title` LIKE '%match% OR  `body` NOT LIKE '%match%' ESCAPE '!'\n\n**$this->db->group_by()**\n\nPermits you to write the GROUP BY portion of your query::\n\n\t$this->db->group_by(\"title\"); // Produces: GROUP BY title\n\nYou can also pass an array of multiple values as well::\n\n\t$this->db->group_by(array(\"title\", \"date\"));  // Produces: GROUP BY title, date\n\n.. note:: group_by() was formerly known as groupby(), which has been\n\tremoved.\n\n**$this->db->distinct()**\n\nAdds the \"DISTINCT\" keyword to a query\n\n::\n\n\t$this->db->distinct();\n\t$this->db->get('table'); // Produces: SELECT DISTINCT * FROM table\n\n**$this->db->having()**\n\nPermits you to write the HAVING portion of your query. There are 2\npossible syntaxes, 1 argument or 2::\n\n\t$this->db->having('user_id = 45');  // Produces: HAVING user_id = 45\n\t$this->db->having('user_id',  45);  // Produces: HAVING user_id = 45\n\nYou can also pass an array of multiple values as well::\n\n\t$this->db->having(array('title =' => 'My Title', 'id <' => $id));\n\t// Produces: HAVING title = 'My Title', id < 45\n\n\nIf you are using a database that CodeIgniter escapes queries for, you\ncan prevent escaping content by passing an optional third argument, and\nsetting it to FALSE.\n\n::\n\n\t$this->db->having('user_id',  45);  // Produces: HAVING `user_id` = 45 in some databases such as MySQL\n\t$this->db->having('user_id',  45, FALSE);  // Produces: HAVING user_id = 45\n\n\n**$this->db->or_having()**\n\nIdentical to having(), only separates multiple clauses with \"OR\".\n\n**$this->db->having_in()**\n\nGenerates a HAVING field IN ('item', 'item') SQL query joined with AND if\nappropriate\n\n::\n\n\t$names = array('Frank', 'Todd', 'James');\n\t$this->db->having_in('username', $names);\n\t// Produces: HAVING username IN ('Frank', 'Todd', 'James')\n\n\n**$this->db->or_having_in()**\n\nGenerates a HAVING field IN ('item', 'item') SQL query joined with OR if\nappropriate\n\n::\n\n\t$names = array('Frank', 'Todd', 'James');\n\t$this->db->or_having_in('username', $names);\n\t// Produces: OR username IN ('Frank', 'Todd', 'James')\n\n**$this->db->having_not_in()**\n\nGenerates a HAVING field NOT IN ('item', 'item') SQL query joined with\nAND if appropriate\n\n::\n\n\t$names = array('Frank', 'Todd', 'James');\n\t$this->db->having_not_in('username', $names);\n\t// Produces: HAVING username NOT IN ('Frank', 'Todd', 'James')\n\n\n**$this->db->or_having_not_in()**\n\nGenerates a HAVING field NOT IN ('item', 'item') SQL query joined with OR\nif appropriate\n\n::\n\n\t$names = array('Frank', 'Todd', 'James');\n\t$this->db->or_having_not_in('username', $names);\n\t// Produces: OR username NOT IN ('Frank', 'Todd', 'James')\n\n****************\nOrdering results\n****************\n\n**$this->db->order_by()**\n\nLets you set an ORDER BY clause.\n\nThe first parameter contains the name of the column you would like to order by.\n\nThe second parameter lets you set the direction of the result.\nOptions are **ASC**, **DESC** AND **RANDOM**.\n\n::\n\n\t$this->db->order_by('title', 'DESC');\n\t// Produces: ORDER BY `title` DESC\n\nYou can also pass your own string in the first parameter::\n\n\t$this->db->order_by('title DESC, name ASC');\n\t// Produces: ORDER BY `title` DESC, `name` ASC\n\nOr multiple function calls can be made if you need multiple fields.\n\n::\n\n\t$this->db->order_by('title', 'DESC');\n\t$this->db->order_by('name', 'ASC');\n\t// Produces: ORDER BY `title` DESC, `name` ASC\n\nIf you choose the **RANDOM** direction option, then the first parameters will\nbe ignored, unless you specify a numeric seed value.\n\n::\n\n\t$this->db->order_by('title', 'RANDOM');\n\t// Produces: ORDER BY RAND()\n\n\t$this->db->order_by(42, 'RANDOM');\n\t// Produces: ORDER BY RAND(42)\n\n.. note:: order_by() was formerly known as orderby(), which has been\n\tremoved.\n\n.. note:: Random ordering is not currently supported in Oracle and\n\twill default to ASC instead.\n\n****************************\nLimiting or Counting Results\n****************************\n\n**$this->db->limit()**\n\nLets you limit the number of rows you would like returned by the query::\n\n\t$this->db->limit(10);  // Produces: LIMIT 10\n\nThe second parameter lets you set a result offset.\n\n::\n\n\t$this->db->limit(10, 20);  // Produces: LIMIT 20, 10 (in MySQL.  Other databases have slightly different syntax)\n\n**$this->db->count_all_results()**\n\nPermits you to determine the number of rows in a particular Active\nRecord query. Queries will accept Query Builder restrictors such as\n``where()``, ``or_where()``, ``like()``, ``or_like()``, etc. Example::\n\n\techo $this->db->count_all_results('my_table');  // Produces an integer, like 25\n\t$this->db->like('title', 'match');\n\t$this->db->from('my_table');\n\techo $this->db->count_all_results(); // Produces an integer, like 17\n\nHowever, this method also resets any field values that you may have passed\nto ``select()``. If you need to keep them, you can pass ``FALSE`` as the\nsecond parameter::\n\n\techo $this->db->count_all_results('my_table', FALSE);\n\n**$this->db->count_all()**\n\nPermits you to determine the number of rows in a particular table.\nSubmit the table name in the first parameter. Example::\n\n\techo $this->db->count_all('my_table');  // Produces an integer, like 25\n\n**************\nQuery grouping\n**************\n\nQuery grouping allows you to create groups of WHERE clauses by enclosing them in parentheses. This will allow\nyou to create queries with complex WHERE clauses. Nested groups are supported. Example::\n\n\t$this->db->select('*')->from('my_table')\n\t\t->group_start()\n\t\t\t->where('a', 'a')\n\t\t\t->or_group_start()\n\t\t\t\t->where('b', 'b')\n\t\t\t\t->where('c', 'c')\n\t\t\t->group_end()\n\t\t->group_end()\n\t\t->where('d', 'd')\n\t->get();\n\n\t// Generates:\n\t// SELECT * FROM (`my_table`) WHERE ( `a` = 'a' OR ( `b` = 'b' AND `c` = 'c' ) ) AND `d` = 'd'\n\n.. note:: groups need to be balanced, make sure every group_start() is matched by a group_end().\n\n**$this->db->group_start()**\n\nStarts a new group by adding an opening parenthesis to the WHERE clause of the query.\n\n**$this->db->or_group_start()**\n\nStarts a new group by adding an opening parenthesis to the WHERE clause of the query, prefixing it with 'OR'.\n\n**$this->db->not_group_start()**\n\nStarts a new group by adding an opening parenthesis to the WHERE clause of the query, prefixing it with 'NOT'.\n\n**$this->db->or_not_group_start()**\n\nStarts a new group by adding an opening parenthesis to the WHERE clause of the query, prefixing it with 'OR NOT'.\n\n**$this->db->group_end()**\n\nEnds the current group by adding an closing parenthesis to the WHERE clause of the query.\n\n**************\nInserting Data\n**************\n\n**$this->db->insert()**\n\nGenerates an insert string based on the data you supply, and runs the\nquery. You can either pass an **array** or an **object** to the\nfunction. Here is an example using an array::\n\n\t$data = array(\n\t\t'title' => 'My title',\n\t\t'name' => 'My Name',\n\t\t'date' => 'My date'\n\t);\n\n\t$this->db->insert('mytable', $data);\n\t// Produces: INSERT INTO mytable (title, name, date) VALUES ('My title', 'My name', 'My date')\n\nThe first parameter will contain the table name, the second is an\nassociative array of values.\n\nHere is an example using an object::\n\n\t/*\n\tclass Myclass {\n\t\tpublic $title = 'My Title';\n\t\tpublic $content = 'My Content';\n\t\tpublic $date = 'My Date';\n\t}\n\t*/\n\n\t$object = new Myclass;\n\t$this->db->insert('mytable', $object);\n\t// Produces: INSERT INTO mytable (title, content, date) VALUES ('My Title', 'My Content', 'My Date')\n\nThe first parameter will contain the table name, the second is an\nobject.\n\n.. note:: All values are escaped automatically producing safer queries.\n\n**$this->db->get_compiled_insert()**\n\nCompiles the insertion query just like $this->db->insert() but does not\n*run* the query. This method simply returns the SQL query as a string.\n\nExample::\n\n\t$data = array(\n\t\t'title' => 'My title',\n\t\t'name'  => 'My Name',\n\t\t'date'  => 'My date'\n\t);\n\n\t$sql = $this->db->set($data)->get_compiled_insert('mytable');\n\techo $sql;\n\n\t// Produces string: INSERT INTO mytable (`title`, `name`, `date`) VALUES ('My title', 'My name', 'My date')\n\nThe second parameter enables you to set whether or not the query builder query\nwill be reset (by default it will be--just like $this->db->insert())::\n\n\techo $this->db->set('title', 'My Title')->get_compiled_insert('mytable', FALSE);\n\n\t// Produces string: INSERT INTO mytable (`title`) VALUES ('My Title')\n\n\techo $this->db->set('content', 'My Content')->get_compiled_insert();\n\n\t// Produces string: INSERT INTO mytable (`title`, `content`) VALUES ('My Title', 'My Content')\n\nThe key thing to notice in the above example is that the second query did not\nutilize `$this->db->from()` nor did it pass a table name into the first\nparameter. The reason this worked is because the query has not been executed\nusing `$this->db->insert()` which resets values or reset directly using\n`$this->db->reset_query()`.\n\n.. note:: This method doesn't work for batched inserts.\n\n**$this->db->insert_batch()**\n\nGenerates an insert string based on the data you supply, and runs the\nquery. You can either pass an **array** or an **object** to the\nfunction. Here is an example using an array::\n\n\t$data = array(\n\t\tarray(\n\t\t\t'title' => 'My title',\n\t\t\t'name' => 'My Name',\n\t\t\t'date' => 'My date'\n\t\t),\n\t\tarray(\n\t\t\t'title' => 'Another title',\n\t\t\t'name' => 'Another Name',\n\t\t\t'date' => 'Another date'\n\t\t)\n\t);\n\n\t$this->db->insert_batch('mytable', $data);\n\t// Produces: INSERT INTO mytable (title, name, date) VALUES ('My title', 'My name', 'My date'),  ('Another title', 'Another name', 'Another date')\n\nThe first parameter will contain the table name, the second is an\nassociative array of values.\n\n.. note:: All values are escaped automatically producing safer queries.\n\n*************\nUpdating Data\n*************\n\n**$this->db->replace()**\n\nThis method executes a REPLACE statement, which is basically the SQL\nstandard for (optional) DELETE + INSERT, using *PRIMARY* and *UNIQUE*\nkeys as the determining factor.\nIn our case, it will save you from the need to implement complex\nlogics with different combinations of  ``select()``, ``update()``,\n``delete()`` and ``insert()`` calls.\n\nExample::\n\n\t$data = array(\n\t\t'title' => 'My title',\n\t\t'name'  => 'My Name',\n\t\t'date'  => 'My date'\n\t);\n\n\t$this->db->replace('table', $data);\n\n\t// Executes: REPLACE INTO mytable (title, name, date) VALUES ('My title', 'My name', 'My date')\n\nIn the above example, if we assume that the *title* field is our primary\nkey, then if a row containing 'My title' as the *title* value, that row\nwill be deleted with our new row data replacing it.\n\nUsage of the ``set()`` method is also allowed and all fields are\nautomatically escaped, just like with ``insert()``.\n\n**$this->db->set()**\n\nThis function enables you to set values for inserts or updates.\n\n**It can be used instead of passing a data array directly to the insert\nor update functions:**\n\n::\n\n\t$this->db->set('name', $name);\n\t$this->db->insert('mytable');  // Produces: INSERT INTO mytable (`name`) VALUES ('{$name}')\n\nIf you use multiple function called they will be assembled properly\nbased on whether you are doing an insert or an update::\n\n\t$this->db->set('name', $name);\n\t$this->db->set('title', $title);\n\t$this->db->set('status', $status);\n\t$this->db->insert('mytable');\n\n**set()** will also accept an optional third parameter (``$escape``), that\nwill prevent data from being escaped if set to FALSE. To illustrate the\ndifference, here is ``set()`` used both with and without the escape\nparameter.\n\n::\n\n\t$this->db->set('field', 'field+1', FALSE);\n\t$this->db->where('id', 2);\n\t$this->db->update('mytable'); // gives UPDATE mytable SET field = field+1 WHERE id = 2\n\n\t$this->db->set('field', 'field+1');\n\t$this->db->where('id', 2);\n\t$this->db->update('mytable'); // gives UPDATE `mytable` SET `field` = 'field+1' WHERE `id` = 2\n\nYou can also pass an associative array to this function::\n\n\t$array = array(\n\t\t'name' => $name,\n\t\t'title' => $title,\n\t\t'status' => $status\n\t);\n\n\t$this->db->set($array);\n\t$this->db->insert('mytable');\n\nOr an object::\n\n\t/*\n\tclass Myclass {\n\t\tpublic $title = 'My Title';\n\t\tpublic $content = 'My Content';\n\t\tpublic $date = 'My Date';\n\t}\n\t*/\n\n\t$object = new Myclass;\n\t$this->db->set($object);\n\t$this->db->insert('mytable');\n\n**$this->db->update()**\n\nGenerates an update string and runs the query based on the data you\nsupply. You can pass an **array** or an **object** to the function. Here\nis an example using an array::\n\n\t$data = array(\n\t\t'title' => $title,\n\t\t'name' => $name,\n\t\t'date' => $date\n\t);\n\n\t$this->db->where('id', $id);\n\t$this->db->update('mytable', $data);\n\t// Produces:\n\t//\n\t//\tUPDATE mytable\n\t//\tSET title = '{$title}', name = '{$name}', date = '{$date}'\n\t//\tWHERE id = $id\n\nOr you can supply an object::\n\n\t/*\n\tclass Myclass {\n\t\tpublic $title = 'My Title';\n\t\tpublic $content = 'My Content';\n\t\tpublic $date = 'My Date';\n\t}\n\t*/\n\n\t$object = new Myclass;\n\t$this->db->where('id', $id);\n\t$this->db->update('mytable', $object);\n\t// Produces:\n\t//\n\t// UPDATE `mytable`\n\t// SET `title` = '{$title}', `name` = '{$name}', `date` = '{$date}'\n\t// WHERE id = `$id`\n\n.. note:: All values are escaped automatically producing safer queries.\n\nYou'll notice the use of the $this->db->where() function, enabling you\nto set the WHERE clause. You can optionally pass this information\ndirectly into the update function as a string::\n\n\t$this->db->update('mytable', $data, \"id = 4\");\n\nOr as an array::\n\n\t$this->db->update('mytable', $data, array('id' => $id));\n\nYou may also use the $this->db->set() function described above when\nperforming updates.\n\n**$this->db->update_batch()**\n\nGenerates an update string based on the data you supply, and runs the query.\nYou can either pass an **array** or an **object** to the function.\nHere is an example using an array::\n\n\t$data = array(\n\t   array(\n\t      'title' => 'My title' ,\n\t      'name' => 'My Name 2' ,\n\t      'date' => 'My date 2'\n\t   ),\n\t   array(\n\t      'title' => 'Another title' ,\n\t      'name' => 'Another Name 2' ,\n\t      'date' => 'Another date 2'\n\t   )\n\t);\n\n\t$this->db->update_batch('mytable', $data, 'title');\n\n\t// Produces:\n\t// UPDATE `mytable` SET `name` = CASE\n\t// WHEN `title` = 'My title' THEN 'My Name 2'\n\t// WHEN `title` = 'Another title' THEN 'Another Name 2'\n\t// ELSE `name` END,\n\t// `date` = CASE\n\t// WHEN `title` = 'My title' THEN 'My date 2'\n\t// WHEN `title` = 'Another title' THEN 'Another date 2'\n\t// ELSE `date` END\n\t// WHERE `title` IN ('My title','Another title')\n\nThe first parameter will contain the table name, the second is an associative\narray of values, the third parameter is the where key.\n\n.. note:: All values are escaped automatically producing safer queries.\n\n.. note:: ``affected_rows()`` won't give you proper results with this method,\n\tdue to the very nature of how it works. Instead, ``update_batch()``\n\treturns the number of rows affected.\n\n**$this->db->get_compiled_update()**\n\nThis works exactly the same way as ``$this->db->get_compiled_insert()`` except\nthat it produces an UPDATE SQL string instead of an INSERT SQL string.\n\nFor more information view documentation for `$this->db->get_compiled_insert()`.\n\n.. note:: This method doesn't work for batched updates.\n\n*************\nDeleting Data\n*************\n\n**$this->db->delete()**\n\nGenerates a delete SQL string and runs the query.\n\n::\n\n\t$this->db->delete('mytable', array('id' => $id));  // Produces: // DELETE FROM mytable  // WHERE id = $id\n\nThe first parameter is the table name, the second is the where clause.\nYou can also use the where() or or_where() functions instead of passing\nthe data to the second parameter of the function::\n\n\t$this->db->where('id', $id);\n\t$this->db->delete('mytable');\n\n\t// Produces:\n\t// DELETE FROM mytable\n\t// WHERE id = $id\n\n\nAn array of table names can be passed into delete() if you would like to\ndelete data from more than 1 table.\n\n::\n\n\t$tables = array('table1', 'table2', 'table3');\n\t$this->db->where('id', '5');\n\t$this->db->delete($tables);\n\n\nIf you want to delete all data from a table, you can use the truncate()\nfunction, or empty_table().\n\n**$this->db->empty_table()**\n\nGenerates a delete SQL string and runs the\nquery.::\n\n\t  $this->db->empty_table('mytable'); // Produces: DELETE FROM mytable\n\n**$this->db->truncate()**\n\nGenerates a truncate SQL string and runs the query.\n\n::\n\n\t$this->db->from('mytable');\n\t$this->db->truncate();\n\n\t// or\n\n\t$this->db->truncate('mytable');\n\n\t// Produce:\n\t// TRUNCATE mytable\n\n.. note:: If the TRUNCATE command isn't available, truncate() will\n\texecute as \"DELETE FROM table\".\n\n**$this->db->get_compiled_delete()**\n\nThis works exactly the same way as ``$this->db->get_compiled_insert()`` except\nthat it produces a DELETE SQL string instead of an INSERT SQL string.\n\nFor more information view documentation for $this->db->get_compiled_insert().\n\n***************\nMethod Chaining\n***************\n\nMethod chaining allows you to simplify your syntax by connecting\nmultiple functions. Consider this example::\n\n\t$query = $this->db->select('title')\n\t\t\t->where('id', $id)\n\t\t\t->limit(10, 20)\n\t\t\t->get('mytable');\n\n.. _ar-caching:\n\n*********************\nQuery Builder Caching\n*********************\n\nWhile not \"true\" caching, Query Builder enables you to save (or \"cache\")\ncertain parts of your queries for reuse at a later point in your\nscript's execution. Normally, when an Query Builder call is completed,\nall stored information is reset for the next call. With caching, you can\nprevent this reset, and reuse information easily.\n\nCached calls are cumulative. If you make 2 cached select() calls, and\nthen 2 uncached select() calls, this will result in 4 select() calls.\nThere are three Caching functions available:\n\n**$this->db->start_cache()**\n\nThis function must be called to begin caching. All Query Builder queries\nof the correct type (see below for supported queries) are stored for\nlater use.\n\n**$this->db->stop_cache()**\n\nThis function can be called to stop caching.\n\n**$this->db->flush_cache()**\n\nThis function deletes all items from the Query Builder cache.\n\nAn example of caching\n---------------------\n\nHere's a usage example::\n\n\t$this->db->start_cache();\n\t$this->db->select('field1');\n\t$this->db->stop_cache();\n\t$this->db->get('tablename');\n\t//Generates: SELECT `field1` FROM (`tablename`)\n\n\t$this->db->select('field2');\n\t$this->db->get('tablename');\n\t//Generates:  SELECT `field1`, `field2` FROM (`tablename`)\n\n\t$this->db->flush_cache();\n\t$this->db->select('field2');\n\t$this->db->get('tablename');\n\t//Generates:  SELECT `field2` FROM (`tablename`)\n\n\n.. note:: The following statements can be cached: select, from, join,\n\twhere, like, group_by, having, order_by\n\n\n***********************\nResetting Query Builder\n***********************\n\n**$this->db->reset_query()**\n\nResetting Query Builder allows you to start fresh with your query without\nexecuting it first using a method like $this->db->get() or $this->db->insert().\nJust like the methods that execute a query, this will *not* reset items you've\ncached using `Query Builder Caching`_.\n\nThis is useful in situations where you are using Query Builder to generate SQL\n(ex. ``$this->db->get_compiled_select()``) but then choose to, for instance,\nrun the query::\n\n\t// Note that the second parameter of the get_compiled_select method is FALSE\n\t$sql = $this->db->select(array('field1','field2'))\n\t\t\t\t\t->where('field3',5)\n\t\t\t\t\t->get_compiled_select('mytable', FALSE);\n\n\t// ...\n\t// Do something crazy with the SQL code... like add it to a cron script for\n\t// later execution or something...\n\t// ...\n\n\t$data = $this->db->get()->result_array();\n\n\t// Would execute and return an array of results of the following query:\n\t// SELECT field1, field1 from mytable where field3 = 5;\n\n.. note:: Double calls to ``get_compiled_select()`` while you're using the\n\tQuery Builder Caching functionality and NOT resetting your queries\n\twill results in the cache being merged twice. That in turn will\n\ti.e. if you're caching a ``select()`` - select the same field twice.\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_DB_query_builder\n\n\t.. php:method:: reset_query()\n\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tResets the current Query Builder state.  Useful when you want\n\t\tto build a query that can be cancelled under certain conditions.\n\n\t.. php:method:: start_cache()\n\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tStarts the Query Builder cache.\n\n\t.. php:method:: stop_cache()\n\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tStops the Query Builder cache.\n\n\t.. php:method:: flush_cache()\n\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tEmpties the Query Builder cache.\n\n\t.. php:method:: set_dbprefix([$prefix = ''])\n\n\t\t:param\tstring\t$prefix: The new prefix to use\n\t\t:returns:\tThe DB prefix in use\n\t\t:rtype:\tstring\n\n\t\tSets the database prefix, without having to reconnect.\n\n\t.. php:method:: dbprefix([$table = ''])\n\n\t\t:param\tstring\t$table: The table name to prefix\n\t\t:returns:\tThe prefixed table name\n\t\t:rtype:\tstring\n\n\t\tPrepends a database prefix, if one exists in configuration.\n\n\t.. php:method:: count_all_results([$table = '', [$reset = TRUE]])\n\n\t\t:param\tstring\t$table: Table name\n\t\t:param\tbool\t$reset: Whether to reset values for SELECTs\n\t\t:returns:\tNumber of rows in the query result\n\t\t:rtype:\tint\n\n\t\tGenerates a platform-specific query string that counts\n\t\tall records returned by an Query Builder query.\n\n\t.. php:method:: get([$table = ''[, $limit = NULL[, $offset = NULL]]])\n\n\t\t:param\tstring\t$table: The table to query\n\t\t:param\tint\t$limit: The LIMIT clause\n\t\t:param\tint\t$offset: The OFFSET clause\n\t\t:returns:\tCI_DB_result instance (method chaining)\n\t\t:rtype:\tCI_DB_result\n\n\t\tCompiles and runs SELECT statement based on the already\n\t\tcalled Query Builder methods.\n\n\t.. php:method:: get_where([$table = ''[, $where = NULL[, $limit = NULL[, $offset = NULL]]]])\n\n\t\t:param\tmixed\t$table: The table(s) to fetch data from; string or array\n\t\t:param\tstring\t$where: The WHERE clause\n\t\t:param\tint\t$limit: The LIMIT clause\n\t\t:param\tint\t$offset: The OFFSET clause\n\t\t:returns:\tCI_DB_result instance (method chaining)\n\t\t:rtype:\tCI_DB_result\n\n\t\tSame as ``get()``, but also allows the WHERE to be added directly.\n\n\t.. php:method:: select([$select = '*'[, $escape = NULL]])\n\n\t\t:param\tstring\t$select: The SELECT portion of a query\n\t\t:param\tbool\t$escape: Whether to escape values and identifiers\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tAdds a SELECT clause to a query.\n\n\t.. php:method:: select_avg([$select = ''[, $alias = '']])\n\n\t\t:param\tstring\t$select: Field to compute the average of\n\t\t:param\tstring\t$alias: Alias for the resulting value name\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tAdds a SELECT AVG(field) clause to a query.\n\n\t.. php:method:: select_max([$select = ''[, $alias = '']])\n\n\t\t:param\tstring\t$select: Field to compute the maximum of\n\t\t:param\tstring\t$alias: Alias for the resulting value name\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tAdds a SELECT MAX(field) clause to a query.\n\n\t.. php:method:: select_min([$select = ''[, $alias = '']])\n\n\t\t:param\tstring\t$select: Field to compute the minimum of\n\t\t:param\tstring\t$alias: Alias for the resulting value name\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tAdds a SELECT MIN(field) clause to a query.\n\n\t.. php:method:: select_sum([$select = ''[, $alias = '']])\n\n\t\t:param\tstring\t$select: Field to compute the sum of\n\t\t:param\tstring\t$alias: Alias for the resulting value name\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tAdds a SELECT SUM(field) clause to a query.\n\n\t.. php:method:: distinct([$val = TRUE])\n\n\t\t:param\tbool\t$val: Desired value of the \"distinct\" flag\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tSets a flag which tells the query builder to add\n\t\ta DISTINCT clause to the SELECT portion of the query.\n\n\t.. php:method:: from($from)\n\n\t\t:param\tmixed\t$from: Table name(s); string or array\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tSpecifies the FROM clause of a query.\n\n\t.. php:method:: join($table, $cond[, $type = ''[, $escape = NULL]])\n\n\t\t:param\tstring\t$table: Table name to join\n\t\t:param\tstring\t$cond: The JOIN ON condition\n\t\t:param\tstring\t$type: The JOIN type\n\t\t:param\tbool\t$escape: Whether to escape values and identifiers\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tAdds a JOIN clause to a query.\n\n\t.. php:method:: where($key[, $value = NULL[, $escape = NULL]])\n\n\t\t:param\tmixed\t$key: Name of field to compare, or associative array\n\t\t:param\tmixed\t$value: If a single key, compared to this value\n\t\t:param\tbool\t$escape: Whether to escape values and identifiers\n\t\t:returns:\tDB_query_builder instance\n\t\t:rtype:\tobject\n\n\t\tGenerates the WHERE portion of the query.\n                Separates multiple calls with 'AND'.\n\n\t.. php:method:: or_where($key[, $value = NULL[, $escape = NULL]])\n\n\t\t:param\tmixed\t$key: Name of field to compare, or associative array\n\t\t:param\tmixed\t$value: If a single key, compared to this value\n\t\t:param\tbool\t$escape: Whether to escape values and identifiers\n\t\t:returns:\tDB_query_builder instance\n\t\t:rtype:\tobject\n\n\t\tGenerates the WHERE portion of the query.\n                Separates multiple calls with 'OR'.\n\n\t.. php:method:: or_where_in([$key[, array $values[, $escape = NULL]]])\n\n\t\t:param\tstring\t$key: The field to search\n\t\t:param\tarray\t$values: The values searched on\n\t\t:param\tbool\t$escape: Whether to escape values and identifiers\n\t\t:returns:\tDB_query_builder instance\n\t\t:rtype:\tobject\n\n\t\tGenerates a WHERE field IN('item', 'item') SQL query,\n                joined with 'OR' if appropriate.\n\n\t.. php:method:: or_where_not_in([$key[, array $values[, $escape = NULL]]])\n\n\t\t:param\tstring\t$key: The field to search\n\t\t:param\tarray\t$values: The values searched on\n\t\t:param\tbool\t$escape: Whether to escape values and identifiers\n\t\t:returns:\tDB_query_builder instance\n\t\t:rtype:\tobject\n\n\t\tGenerates a WHERE field NOT IN('item', 'item') SQL query,\n                joined with 'OR' if appropriate.\n\n\t.. php:method:: where_in([$key[, array $values[, $escape = NULL]]])\n\n\t\t:param\tstring\t$key: Name of field to examine\n\t\t:param\tarray\t$values: Array of target values\n\t\t:param\tbool\t$escape: Whether to escape values and identifiers\n\t\t:returns:\tDB_query_builder instance\n\t\t:rtype:\tobject\n\n\t\tGenerates a WHERE field IN('item', 'item') SQL query,\n                joined with 'AND' if appropriate.\n\n\t.. php:method:: where_not_in([$key[, array $values[, $escape = NULL]]])\n\n\t\t:param\tstring\t$key: Name of field to examine\n\t\t:param\tarray\t$values: Array of target values\n\t\t:param\tbool\t$escape: Whether to escape values and identifiers\n\t\t:returns:\tDB_query_builder instance\n\t\t:rtype:\tobject\n\n\t\tGenerates a WHERE field NOT IN('item', 'item') SQL query,\n                joined with 'AND' if appropriate.\n\n\t.. php:method:: group_start()\n\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tStarts a group expression, using ANDs for the conditions inside it.\n\n\t.. php:method:: or_group_start()\n\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tStarts a group expression, using ORs for the conditions inside it.\n\n\t.. php:method:: not_group_start()\n\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tStarts a group expression, using AND NOTs for the conditions inside it.\n\n\t.. php:method:: or_not_group_start()\n\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tStarts a group expression, using OR NOTs for the conditions inside it.\n\n\t.. php:method:: group_end()\n\n\t\t:returns:\tDB_query_builder instance\n\t\t:rtype:\tobject\n\n\t\tEnds a group expression.\n\n\t.. php:method:: like($field[, $match = ''[, $side = 'both'[, $escape = NULL]]])\n\n\t\t:param\tstring\t$field: Field name\n\t\t:param\tstring\t$match: Text portion to match\n\t\t:param\tstring\t$side: Which side of the expression to put the '%' wildcard on\n\t\t:param\tbool\t$escape: Whether to escape values and identifiers\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tAdds a LIKE clause to a query, separating multiple calls with AND.\n\n\t.. php:method:: or_like($field[, $match = ''[, $side = 'both'[, $escape = NULL]]])\n\n\t\t:param\tstring\t$field: Field name\n\t\t:param\tstring\t$match: Text portion to match\n\t\t:param\tstring\t$side: Which side of the expression to put the '%' wildcard on\n\t\t:param\tbool\t$escape: Whether to escape values and identifiers\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tAdds a LIKE clause to a query, separating multiple class with OR.\n\n\t.. php:method:: not_like($field[, $match = ''[, $side = 'both'[, $escape = NULL]]])\n\n\t\t:param\tstring\t$field: Field name\n\t\t:param\tstring\t$match: Text portion to match\n\t\t:param\tstring\t$side: Which side of the expression to put the '%' wildcard on\n\t\t:param\tbool\t$escape: Whether to escape values and identifiers\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tAdds a NOT LIKE clause to a query, separating multiple calls with AND.\n\n\t.. php:method:: or_not_like($field[, $match = ''[, $side = 'both'[, $escape = NULL]]])\n\n\t\t:param\tstring\t$field: Field name\n\t\t:param\tstring\t$match: Text portion to match\n\t\t:param\tstring\t$side: Which side of the expression to put the '%' wildcard on\n\t\t:param\tbool\t$escape: Whether to escape values and identifiers\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tAdds a NOT LIKE clause to a query, separating multiple calls with OR.\n\n\t.. php:method:: having($key[, $value = NULL[, $escape = NULL]])\n\n\t\t:param\tmixed\t$key: Identifier (string) or associative array of field/value pairs\n\t\t:param\tstring\t$value: Value sought if $key is an identifier\n\t\t:param\tstring\t$escape: Whether to escape values and identifiers\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tAdds a HAVING clause to a query, separating multiple calls with AND.\n\n\t.. php:method:: or_having($key[, $value = NULL[, $escape = NULL]])\n\n\t\t:param\tmixed\t$key: Identifier (string) or associative array of field/value pairs\n\t\t:param\tstring\t$value: Value sought if $key is an identifier\n\t\t:param\tstring\t$escape: Whether to escape values and identifiers\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tAdds a HAVING clause to a query, separating multiple calls with OR.\n\n\t.. php:method:: or_having_in([$key[, array $values[, $escape = NULL]]])\n\n\t\t:param\tstring\t$key: The field to search\n\t\t:param\tarray\t$values: The values searched on\n\t\t:param\tbool\t$escape: Whether to escape values and identifiers\n\t\t:returns:\tDB_query_builder instance\n\t\t:rtype:\tobject\n\n\t\tGenerates a HAVING field IN('item', 'item') SQL query,\n                joined with 'OR' if appropriate.\n\n\t.. php:method:: or_having_not_in([$key[, array $values[, $escape = NULL]]])\n\n\t\t:param\tstring\t$key: The field to search\n\t\t:param\tarray\t$values: The values searched on\n\t\t:param\tbool\t$escape: Whether to escape values and identifiers\n\t\t:returns:\tDB_query_builder instance\n\t\t:rtype:\tobject\n\n\t\tGenerates a HAVING field NOT IN('item', 'item') SQL query,\n                joined with 'OR' if appropriate.\n\n\t.. php:method:: having_in([$key[, array $values[, $escape = NULL]]])\n\n\t\t:param\tstring\t$key: Name of field to examine\n\t\t:param\tarray\t$values: Array of target values\n\t\t:param\tbool\t$escape: Whether to escape values and identifiers\n\t\t:returns:\tDB_query_builder instance\n\t\t:rtype:\tobject\n\n\t\tGenerates a HAVING field IN('item', 'item') SQL query,\n                joined with 'AND' if appropriate.\n\n\t.. php:method:: having_not_in([$key[, array $values[, $escape = NULL]]])\n\n\t\t:param\tstring\t$key: Name of field to examine\n\t\t:param\tarray\t$values: Array of target values\n\t\t:param\tbool\t$escape: Whether to escape values and identifiers\n\t\t:returns:\tDB_query_builder instance\n\t\t:rtype:\tobject\n\n\t\tGenerates a HAVING field NOT IN('item', 'item') SQL query,\n                joined with 'AND' if appropriate.\n\n\t.. php:method:: group_by($by[, $escape = NULL])\n\n\t\t:param\tmixed\t$by: Field(s) to group by; string or array\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tAdds a GROUP BY clause to a query.\n\n\t.. php:method:: order_by($orderby[, $direction = ''[, $escape = NULL]])\n\n\t\t:param\tstring\t$orderby: Field to order by\n\t\t:param\tstring\t$direction: The order requested - ASC, DESC or random\n\t\t:param\tbool\t$escape: Whether to escape values and identifiers\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tAdds an ORDER BY clause to a query.\n\n\t.. php:method:: limit($value[, $offset = 0])\n\n\t\t:param\tint\t$value: Number of rows to limit the results to\n\t\t:param\tint\t$offset: Number of rows to skip\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tAdds LIMIT and OFFSET clauses to a query.\n\n\t.. php:method:: offset($offset)\n\n\t\t:param\tint\t$offset: Number of rows to skip\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tAdds an OFFSET clause to a query.\n\n\t.. php:method:: set($key[, $value = ''[, $escape = NULL]])\n\n\t\t:param\tmixed\t$key: Field name, or an array of field/value pairs\n\t\t:param\tstring\t$value: Field value, if $key is a single field\n\t\t:param\tbool\t$escape: Whether to escape values and identifiers\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tAdds field/value pairs to be passed later to ``insert()``,\n\t\t``update()`` or ``replace()``.\n\n\t.. php:method:: insert([$table = ''[, $set = NULL[, $escape = NULL]]])\n\n\t\t:param\tstring\t$table: Table name\n\t\t:param\tarray\t$set: An associative array of field/value pairs\n\t\t:param\tbool\t$escape: Whether to escape values and identifiers\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tCompiles and executes an INSERT statement.\n\n\t.. php:method:: insert_batch($table[, $set = NULL[, $escape = NULL[, $batch_size = 100]]])\n\n\t\t:param\tstring\t$table: Table name\n\t\t:param\tarray\t$set: Data to insert\n\t\t:param\tbool\t$escape: Whether to escape values and identifiers\n\t\t:param\tint\t$batch_size: Count of rows to insert at once\n\t\t:returns:\tNumber of rows inserted or FALSE on failure\n\t\t:rtype:\tmixed\n\n\t\tCompiles and executes batch ``INSERT`` statements.\n\n\t\t.. note:: When more than ``$batch_size`` rows are provided, multiple\n\t\t\t``INSERT`` queries will be executed, each trying to insert\n\t\t\tup to ``$batch_size`` rows.\n\n\t.. php:method:: set_insert_batch($key[, $value = ''[, $escape = NULL]])\n\n\t\t:param\tmixed\t$key: Field name or an array of field/value pairs\n\t\t:param\tstring\t$value: Field value, if $key is a single field\n\t\t:param\tbool\t$escape: Whether to escape values and identifiers\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tAdds field/value pairs to be inserted in a table later via ``insert_batch()``.\n\n\t.. php:method:: update([$table = ''[, $set = NULL[, $where = NULL[, $limit = NULL]]]])\n\n\t\t:param\tstring\t$table: Table name\n\t\t:param\tarray\t$set: An associative array of field/value pairs\n\t\t:param\tstring\t$where: The WHERE clause\n\t\t:param\tint\t$limit: The LIMIT clause\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tCompiles and executes an UPDATE statement.\n\n\t.. php:method:: update_batch($table[, $set = NULL[, $value = NULL[, $batch_size = 100]]])\n\n\t\t:param\tstring\t$table: Table name\n\t\t:param\tarray\t$set: Field name, or an associative array of field/value pairs\n\t\t:param\tstring\t$value: Field value, if $set is a single field\n\t\t:param\tint\t$batch_size: Count of conditions to group in a single query\n\t\t:returns:\tNumber of rows updated or FALSE on failure\n\t\t:rtype:\tmixed\n\n\t\tCompiles and executes batch ``UPDATE`` statements.\n\n\t\t.. note:: When more than ``$batch_size`` field/value pairs are provided,\n\t\t\tmultiple queries will be executed, each handling up to\n\t\t\t``$batch_size`` field/value pairs.\n\n\t.. php:method:: set_update_batch($key[, $value = ''[, $escape = NULL]])\n\n\t\t:param\tmixed\t$key: Field name or an array of field/value pairs\n\t\t:param\tstring\t$value: Field value, if $key is a single field\n\t\t:param\tbool\t$escape: Whether to escape values and identifiers\n\t\t:returns:\tCI_DB_query_builder instance (method chaining)\n\t\t:rtype:\tCI_DB_query_builder\n\n\t\tAdds field/value pairs to be updated in a table later via ``update_batch()``.\n\n\t.. php:method:: replace([$table = ''[, $set = NULL]])\n\n\t\t:param\tstring\t$table: Table name\n\t\t:param\tarray\t$set: An associative array of field/value pairs\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tCompiles and executes a REPLACE statement.\n\n\t.. php:method:: delete([$table = ''[, $where = ''[, $limit = NULL[, $reset_data = TRUE]]]])\n\n\t\t:param\tmixed\t$table: The table(s) to delete from; string or array\n\t\t:param\tstring\t$where: The WHERE clause\n\t\t:param\tint\t$limit: The LIMIT clause\n\t\t:param\tbool\t$reset_data: TRUE to reset the query \"write\" clause\n\t\t:returns:\tCI_DB_query_builder instance (method chaining) or FALSE on failure\n\t\t:rtype:\tmixed\n\n\t\tCompiles and executes a DELETE query.\n\n\t.. php:method:: truncate([$table = ''])\n\n\t\t:param\tstring\t$table: Table name\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tExecutes a TRUNCATE statement on a table.\n\n\t\t.. note:: If the database platform in use doesn't support TRUNCATE,\n\t\t\ta DELETE statement will be used instead.\n\n\t.. php:method:: empty_table([$table = ''])\n\n\t\t:param\tstring\t$table: Table name\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tDeletes all records from a table via a DELETE statement.\n\n\t.. php:method:: get_compiled_select([$table = ''[, $reset = TRUE]])\n\n\t\t:param\tstring\t$table: Table name\n\t\t:param\tbool\t$reset: Whether to reset the current QB values or not\n\t\t:returns:\tThe compiled SQL statement as a string\n\t\t:rtype:\tstring\n\n\t\tCompiles a SELECT statement and returns it as a string.\n\n\t.. php:method:: get_compiled_insert([$table = ''[, $reset = TRUE]])\n\n\t\t:param\tstring\t$table: Table name\n\t\t:param\tbool\t$reset: Whether to reset the current QB values or not\n\t\t:returns:\tThe compiled SQL statement as a string\n\t\t:rtype:\tstring\n\n\t\tCompiles an INSERT statement and returns it as a string.\n\n\t.. php:method:: get_compiled_update([$table = ''[, $reset = TRUE]])\n\n\t\t:param\tstring\t$table: Table name\n\t\t:param\tbool\t$reset: Whether to reset the current QB values or not\n\t\t:returns:\tThe compiled SQL statement as a string\n\t\t:rtype:\tstring\n\n\t\tCompiles an UPDATE statement and returns it as a string.\n\n\t.. php:method:: get_compiled_delete([$table = ''[, $reset = TRUE]])\n\n\t\t:param\tstring\t$table: Table name\n\t\t:param\tbool\t$reset: Whether to reset the current QB values or not\n\t\t:returns:\tThe compiled SQL statement as a string\n\t\t:rtype:\tstring\n\n\t\tCompiles a DELETE statement and returns it as a string.\n"
  },
  {
    "path": "user_guide_src/source/database/results.rst",
    "content": "########################\nGenerating Query Results\n########################\n\nThere are several ways to generate query results:\n\n.. contents::\n    :local:\n    :depth: 2\n\n*************\nResult Arrays\n*************\n\n**result()**\n\nThis method returns the query result as an array of **objects**, or\n**an empty array** on failure. Typically you'll use this in a foreach\nloop, like this::\n\n\t$query = $this->db->query(\"YOUR QUERY\");\n\n\tforeach ($query->result() as $row)\n\t{\n\t\techo $row->title;\n\t\techo $row->name;\n\t\techo $row->body;\n\t}\n\nThe above method is an alias of ``result_object()``.\n\nYou can also pass a string to ``result()`` which represents a class to\ninstantiate for each result object (note: this class must be loaded)\n\n::\n\n\t$query = $this->db->query(\"SELECT * FROM users;\");\n\n\tforeach ($query->result('User') as $user)\n\t{\n\t\techo $user->name; // access attributes\n\t\techo $user->reverse_name(); // or methods defined on the 'User' class\n\t}\n\n**result_array()**\n\nThis method returns the query result as a pure array, or an empty\narray when no result is produced. Typically you'll use this in a foreach\nloop, like this::\n\n\t$query = $this->db->query(\"YOUR QUERY\");\n\n\tforeach ($query->result_array() as $row)\n\t{\n\t\techo $row['title'];\n\t\techo $row['name'];\n\t\techo $row['body'];\n\t}\n\n***********\nResult Rows\n***********\n\n**row()**\n\nThis method returns a single result row. If your query has more than\none row, it returns only the first row. The result is returned as an\n**object**. Here's a usage example::\n\n\t$query = $this->db->query(\"YOUR QUERY\");\n\n\t$row = $query->row();\n\n\tif (isset($row))\n\t{\n\t\techo $row->title;\n\t\techo $row->name;\n\t\techo $row->body;\n\t}\n\nIf you want a specific row returned you can submit the row number as a\ndigit in the first parameter::\n\n\t$row = $query->row(5);\n\nYou can also add a second String parameter, which is the name of a class\nto instantiate the row with::\n\n\t$query = $this->db->query(\"SELECT * FROM users LIMIT 1;\");\n\t$row = $query->row(0, 'User');\n\t\n\techo $row->name; // access attributes\n\techo $row->reverse_name(); // or methods defined on the 'User' class\n\n**row_array()**\n\nIdentical to the above ``row()`` method, except it returns an array.\nExample::\n\n\t$query = $this->db->query(\"YOUR QUERY\");\n\n\t$row = $query->row_array();\n\n\tif (isset($row))\n\t{\n\t\techo $row['title'];\n\t\techo $row['name'];\n\t\techo $row['body'];\n\t}\n\nIf you want a specific row returned you can submit the row number as a\ndigit in the first parameter::\n\n\t$row = $query->row_array(5);\n\nIn addition, you can walk forward/backwards/first/last through your\nresults using these variations:\n\n\t| **$row = $query->first_row()**\n\t| **$row = $query->last_row()**\n\t| **$row = $query->next_row()**\n\t| **$row = $query->previous_row()**\n\nBy default they return an object unless you put the word \"array\" in the\nparameter:\n\n\t| **$row = $query->first_row('array')**\n\t| **$row = $query->last_row('array')**\n\t| **$row = $query->next_row('array')**\n\t| **$row = $query->previous_row('array')**\n\n.. note:: All the methods above will load the whole result into memory\n\t(prefetching). Use ``unbuffered_row()`` for processing large\n\tresult sets.\n\n**unbuffered_row()**\n\nThis method returns a single result row without prefetching the whole\nresult in memory as ``row()`` does. If your query has more than one row,\nit returns the current row and moves the internal data pointer ahead. \n\n::\n\n\t$query = $this->db->query(\"YOUR QUERY\");\n\n\twhile ($row = $query->unbuffered_row())\n\t{\t\n\t\techo $row->title;\n\t\techo $row->name;\n\t\techo $row->body;\n\t}\n\nYou can optionally pass 'object' (default) or 'array' in order to specify\nthe returned value's type::\n\n\t$query->unbuffered_row();\t\t// object\n\t$query->unbuffered_row('object');\t// object\n\t$query->unbuffered_row('array');\t// associative array\n\n*********************\nCustom Result Objects\n*********************\n\nYou can have the results returned as an instance of a custom class instead\nof a ``stdClass`` or array, as the ``result()`` and ``result_array()``\nmethods allow. This requires that the class is already loaded into memory.\nThe object will have all values returned from the database set as properties.\nIf these have been declared and are non-public then you should provide a\n``__set()`` method to allow them to be set.\n\nExample::\n\n\tclass User {\n\n\t\tpublic $id;\n\t\tpublic $email;\n\t\tpublic $username;\n\n\t\tprotected $last_login;\n\n\t\tpublic function last_login($format)\n\t\t{\n\t\t\treturn $this->last_login->format($format);\n\t\t}\n\n\t\tpublic function __set($name, $value)\n\t\t{\n\t\t\tif ($name === 'last_login')\n\t\t\t{\n\t\t\t\t$this->last_login = DateTime::createFromFormat('U', $value);\n\t\t\t}\n\t\t}\n\n\t\tpublic function __get($name)\n\t\t{\n\t\t\tif (isset($this->$name))\n\t\t\t{\n\t\t\t\treturn $this->$name;\n\t\t\t}\n\t\t}\n\t}\n\nIn addition to the two methods listed below, the following methods also can\ntake a class name to return the results as: ``first_row()``, ``last_row()``,\n``next_row()``, and ``previous_row()``.\n\n**custom_result_object()**\n\nReturns the entire result set as an array of instances of the class requested.\nThe only parameter is the name of the class to instantiate.\n\nExample::\n\n\t$query = $this->db->query(\"YOUR QUERY\");\n\n\t$rows = $query->custom_result_object('User');\n\n\tforeach ($rows as $row)\n\t{\n\t\techo $row->id;\n\t\techo $row->email;\n\t\techo $row->last_login('Y-m-d');\n\t}\n\n**custom_row_object()**\n\nReturns a single row from your query results. The first parameter is the row\nnumber of the results. The second parameter is the class name to instantiate.\n\nExample::\n\n\t$query = $this->db->query(\"YOUR QUERY\");\n\n\t$row = $query->custom_row_object(0, 'User');\n\n\tif (isset($row))\n\t{\n\t\techo $row->email;   // access attributes\n\t\techo $row->last_login('Y-m-d');   // access class methods\n\t}\n\nYou can also use the ``row()`` method in exactly the same way.\n\nExample::\n\n\t$row = $query->custom_row_object(0, 'User');\n\n*********************\nResult Helper Methods\n*********************\n\n**num_rows()**\n\nThe number of rows returned by the query. Note: In this example, $query\nis the variable that the query result object is assigned to::\n\n\t$query = $this->db->query('SELECT * FROM my_table');\n\n\techo $query->num_rows();\n\n.. note:: Not all database drivers have a native way of getting the total\n\tnumber of rows for a result set. When this is the case, all of\n\tthe data is prefetched and ``count()`` is manually called on the\n\tresulting array in order to achieve the same result.\n\t\n**num_fields()**\n\nThe number of FIELDS (columns) returned by the query. Make sure to call\nthe method using your query result object::\n\n\t$query = $this->db->query('SELECT * FROM my_table');\n\n\techo $query->num_fields();\n\n**free_result()**\n\nIt frees the memory associated with the result and deletes the result\nresource ID. Normally PHP frees its memory automatically at the end of\nscript execution. However, if you are running a lot of queries in a\nparticular script you might want to free the result after each query\nresult has been generated in order to cut down on memory consumption.\n\nExample::\n\n\t$query = $this->db->query('SELECT title FROM my_table');\n\n\tforeach ($query->result() as $row)\n\t{\n\t\techo $row->title;\n\t}\n\n\t$query->free_result();  // The $query result object will no longer be available\n\n\t$query2 = $this->db->query('SELECT name FROM some_table');\n\n\t$row = $query2->row();\n\techo $row->name;\n\t$query2->free_result(); // The $query2 result object will no longer be available\n\n**data_seek()**\n\nThis method sets the internal pointer for the next result row to be\nfetched. It is only useful in combination with ``unbuffered_row()``.\n\nIt accepts a positive integer value, which defaults to 0 and returns\nTRUE on success or FALSE on failure.\n\n::\n\n\t$query = $this->db->query('SELECT `field_name` FROM `table_name`');\n\t$query->data_seek(5); // Skip the first 5 rows\n\t$row = $query->unbuffered_row();\n\n.. note:: Not all database drivers support this feature and will return FALSE.\n\tMost notably - you won't be able to use it with PDO.\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_DB_result\n\n\t.. php:method:: result([$type = 'object'])\n\n\t\t:param\tstring\t$type: Type of requested results - array, object, or class name\n\t\t:returns:\tArray containing the fetched rows\n\t\t:rtype:\tarray\n\n\t\tA wrapper for the ``result_array()``, ``result_object()``\n\t\tand ``custom_result_object()`` methods.\n\n\t\tUsage: see `Result Arrays`_.\n\n\t.. php:method:: result_array()\n\n\t\t:returns:\tArray containing the fetched rows\n\t\t:rtype:\tarray\n\n\t\tReturns the query results as an array of rows, where each\n\t\trow is itself an associative array.\n\n\t\tUsage: see `Result Arrays`_.\n\n\t.. php:method:: result_object()\n\n\t\t:returns:\tArray containing the fetched rows\n\t\t:rtype:\tarray\n\n\t\tReturns the query results as an array of rows, where each\n\t\trow is an object of type ``stdClass``.\n\n\t\tUsage: see `Result Arrays`_.\n\n\t.. php:method:: custom_result_object($class_name)\n\n\t\t:param\tstring\t$class_name: Class name for the resulting rows\n\t\t:returns:\tArray containing the fetched rows\n\t\t:rtype:\tarray\n\n\t\tReturns the query results as an array of rows, where each\n\t\trow is an instance of the specified class.\n\n\t.. php:method:: row([$n = 0[, $type = 'object']])\n\n\t\t:param\tint\t$n: Index of the query results row to be returned\n\t\t:param\tstring\t$type: Type of the requested result - array, object, or class name\n\t\t:returns:\tThe requested row or NULL if it doesn't exist\n\t\t:rtype:\tmixed\n\n\t\tA wrapper for the ``row_array()``, ``row_object() and \n\t\t``custom_row_object()`` methods.\n\n\t\tUsage: see `Result Rows`_.\n\n\t.. php:method:: unbuffered_row([$type = 'object'])\n\n\t\t:param\tstring\t$type: Type of the requested result - array, object, or class name\n\t\t:returns:\tNext row from the result set or NULL if it doesn't exist\n\t\t:rtype:\tmixed\n\n\t\tFetches the next result row and returns it in the\n\t\trequested form.\n\n\t\tUsage: see `Result Rows`_.\n\n\t.. php:method:: row_array([$n = 0])\n\n\t\t:param\tint\t$n: Index of the query results row to be returned\n\t\t:returns:\tThe requested row or NULL if it doesn't exist\n\t\t:rtype:\tarray\n\n\t\tReturns the requested result row as an associative array.\n\n\t\tUsage: see `Result Rows`_.\n\n\t.. php:method:: row_object([$n = 0])\n\n\t\t:param\tint\t$n: Index of the query results row to be returned\n                :returns:\tThe requested row or NULL if it doesn't exist\n\t\t:rtype:\tstdClass\n\n\t\tReturns the requested result row as an object of type\n\t\t``stdClass``.\n\n\t\tUsage: see `Result Rows`_.\n\n\t.. php:method:: custom_row_object($n, $type)\n\n\t\t:param\tint\t$n: Index of the results row to return\n\t\t:param\tstring\t$class_name: Class name for the resulting row\n\t\t:returns:\tThe requested row or NULL if it doesn't exist\n\t\t:rtype:\t$type\n\n\t\tReturns the requested result row as an instance of the\n\t\trequested class.\n\n\t.. php:method:: data_seek([$n = 0])\n\n\t\t:param\tint\t$n: Index of the results row to be returned next\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tMoves the internal results row pointer to the desired offset.\n\n\t\tUsage: see `Result Helper Methods`_.\n\n\t.. php:method:: set_row($key[, $value = NULL])\n\n\t\t:param\tmixed\t$key: Column name or array of key/value pairs\n\t\t:param\tmixed\t$value: Value to assign to the column, $key is a single field name\n\t\t:rtype:\tvoid\n\n\t\tAssigns a value to a particular column.\n\n\t.. php:method:: next_row([$type = 'object'])\n\n\t\t:param\tstring\t$type: Type of the requested result - array, object, or class name\n\t\t:returns:\tNext row of result set, or NULL if it doesn't exist\n\t\t:rtype:\tmixed\n\n\t\tReturns the next row from the result set.\n\n\t.. php:method:: previous_row([$type = 'object'])\n\n\t\t:param\tstring\t$type: Type of the requested result - array, object, or class name\n\t\t:returns:\tPrevious row of result set, or NULL if it doesn't exist\n\t\t:rtype:\tmixed\n\n\t\tReturns the previous row from the result set.\n\n\t.. php:method:: first_row([$type = 'object'])\n\n\t\t:param\tstring\t$type: Type of the requested result - array, object, or class name\n\t\t:returns:\tFirst row of result set, or NULL if it doesn't exist\n\t\t:rtype:\tmixed\n\n\t\tReturns the first row from the result set.\n\n\t.. php:method:: last_row([$type = 'object'])\n\n\t\t:param\tstring\t$type: Type of the requested result - array, object, or class name\n\t\t:returns:\tLast row of result set, or NULL if it doesn't exist\n\t\t:rtype:\tmixed\n\n\t\tReturns the last row from the result set.\n\n\t.. php:method:: num_rows()\n\n\t\t:returns:\tNumber of rows in the result set\n\t\t:rtype:\tint\n\n\t\tReturns the number of rows in the result set.\n\n\t\tUsage: see `Result Helper Methods`_.\n\n\t.. php:method:: num_fields()\n\n\t\t:returns:\tNumber of fields in the result set\n\t\t:rtype:\tint\n\n\t\tReturns the number of fields in the result set.\n\n\t\tUsage: see `Result Helper Methods`_.\n\n\t.. php:method:: field_data()\n\n\t\t:returns:\tArray containing field meta-data\n\t\t:rtype:\tarray\n\n\t\tGenerates an array of ``stdClass`` objects containing\n\t\tfield meta-data.\n\n\t.. php:method:: free_result()\n\n\t\t:rtype:\tvoid\n\n\t\tFrees a result set.\n\n\t\tUsage: see `Result Helper Methods`_.\n\n\t.. php:method:: list_fields()\n\n\t\t:returns:\tArray of column names\n\t\t:rtype:\tarray\n\n\t\tReturns an array containing the field names in the\n\t\tresult set.\n"
  },
  {
    "path": "user_guide_src/source/database/transactions.rst",
    "content": "############\nTransactions\n############\n\nCodeIgniter's database abstraction allows you to use transactions with\ndatabases that support transaction-safe table types. In MySQL, you'll\nneed to be running InnoDB or BDB table types rather than the more common\nMyISAM. Most other database platforms support transactions natively.\n\nIf you are not familiar with transactions we recommend you find a good\nonline resource to learn about them for your particular database. The\ninformation below assumes you have a basic understanding of\ntransactions.\n\nCodeIgniter's Approach to Transactions\n======================================\n\nCodeIgniter utilizes an approach to transactions that is very similar to\nthe process used by the popular database class ADODB. We've chosen that\napproach because it greatly simplifies the process of running\ntransactions. In most cases all that is required are two lines of code.\n\nTraditionally, transactions have required a fair amount of work to\nimplement since they demand that you keep track of your queries and\ndetermine whether to commit or rollback based on the success or failure\nof your queries. This is particularly cumbersome with nested queries. In\ncontrast, we've implemented a smart transaction system that does all\nthis for you automatically (you can also manage your transactions\nmanually if you choose to, but there's really no benefit).\n\nRunning Transactions\n====================\n\nTo run your queries using transactions you will use the\n$this->db->trans_start() and $this->db->trans_complete() functions as\nfollows::\n\n\t$this->db->trans_start();\n\t$this->db->query('AN SQL QUERY...');\n\t$this->db->query('ANOTHER QUERY...');\n\t$this->db->query('AND YET ANOTHER QUERY...');\n\t$this->db->trans_complete();\n\nYou can run as many queries as you want between the start/complete\nfunctions and they will all be committed or rolled back based on success\nor failure of any given query.\n\nStrict Mode\n===========\n\nBy default CodeIgniter runs all transactions in Strict Mode. When strict\nmode is enabled, if you are running multiple groups of transactions, if\none group fails all groups will be rolled back. If strict mode is\ndisabled, each group is treated independently, meaning a failure of one\ngroup will not affect any others.\n\nStrict Mode can be disabled as follows::\n\n\t$this->db->trans_strict(FALSE);\n\nManaging Errors\n===============\n\nIf you have error reporting enabled in your config/database.php file\nyou'll see a standard error message if the commit was unsuccessful. If\ndebugging is turned off, you can manage your own errors like this::\n\n\t$this->db->trans_start();\n\t$this->db->query('AN SQL QUERY...');\n\t$this->db->query('ANOTHER QUERY...');\n\t$this->db->trans_complete();\n\t\n\tif ($this->db->trans_status() === FALSE)\n\t{\n\t\t// generate an error... or use the log_message() function to log your error\n\t}\n\nDisabling Transactions\n======================\n\nIf you would like to disable transactions you can do so using\n``$this->db->trans_off()``::\n\n\t$this->db->trans_off();\n\t\n\t$this->db->trans_start();\n\t$this->db->query('AN SQL QUERY...');\n\t$this->db->trans_complete();\n\nWhen transactions are disabled, your queries will be auto-committed, just as\nthey are when running queries without transactions, practically ignoring\nany calls to ``trans_start()``, ``trans_complete()``, etc.\n\nTest Mode\n=========\n\nYou can optionally put the transaction system into \"test mode\", which\nwill cause your queries to be rolled back -- even if the queries produce\na valid result. To use test mode simply set the first parameter in the\n$this->db->trans_start() function to TRUE::\n\n\t$this->db->trans_start(TRUE); // Query will be rolled back\n\t$this->db->query('AN SQL QUERY...');\n\t$this->db->trans_complete();\n\nRunning Transactions Manually\n=============================\n\nIf you would like to run transactions manually you can do so as follows::\n\n\t$this->db->trans_begin();\n\t\n\t$this->db->query('AN SQL QUERY...');\n\t$this->db->query('ANOTHER QUERY...');\n\t$this->db->query('AND YET ANOTHER QUERY...');\n\t\n\tif ($this->db->trans_status() === FALSE)\n\t{\n\t\t$this->db->trans_rollback();\n\t}\n\telse\n\t{\n\t\t$this->db->trans_commit();\n\t}\n\n.. note:: Make sure to use $this->db->trans_begin() when running manual\n\ttransactions, **NOT** $this->db->trans_start().\n"
  },
  {
    "path": "user_guide_src/source/database/utilities.rst",
    "content": "######################\nDatabase Utility Class\n######################\n\nThe Database Utility Class contains methods that help you manage your\ndatabase.\n\n.. contents::\n    :local:\n    :depth: 2\n\n******************************\nInitializing the Utility Class\n******************************\n\n.. important:: In order to initialize the Utility class, your database\n\tdriver must already be running, since the utilities class relies on it.\n\nLoad the Utility Class as follows::\n\n\t$this->load->dbutil();\n\nYou can also pass another database object to the DB Utility loader, in case\nthe database you want to manage isn't the default one::\n\n\t$this->myutil = $this->load->dbutil($this->other_db, TRUE);\n\nIn the above example, we're passing a custom database object as the first\nparameter and then tell it to return the dbutil object, instead of\nassigning it directly to ``$this->dbutil``.\n\n.. note:: Both of the parameters can be used individually, just pass an empty\n\tvalue as the first one if you wish to skip it.\n\nOnce initialized you will access the methods using the ``$this->dbutil``\nobject::\n\n\t$this->dbutil->some_method();\n\n****************************\nUsing the Database Utilities\n****************************\n\nRetrieve list of database names\n================================\n\nReturns an array of database names::\n\n\t$dbs = $this->dbutil->list_databases();\n\n\tforeach ($dbs as $db)\n\t{\n \t\techo $db;\n\t}\n\n\nDetermine If a Database Exists\n==============================\n\nSometimes it's helpful to know whether a particular database exists.\nReturns a boolean TRUE/FALSE. Usage example::\n\n\tif ($this->dbutil->database_exists('database_name'))\n\t{\n\t\t// some code...\n\t}\n\n.. note:: Replace *database_name* with the name of the database you are\n\tlooking for. This method is case sensitive.\n\nOptimize a Table\n================\n\nPermits you to optimize a table using the table name specified in the\nfirst parameter. Returns TRUE/FALSE based on success or failure::\n\n\tif ($this->dbutil->optimize_table('table_name'))\n\t{\n\t\techo 'Success!';\n\t}\n\n.. note:: Not all database platforms support table optimization. It is\n\tmostly for use with MySQL.\n\nRepair a Table\n==============\n\nPermits you to repair a table using the table name specified in the\nfirst parameter. Returns TRUE/FALSE based on success or failure::\n\n\tif ($this->dbutil->repair_table('table_name'))\n\t{\n\t\techo 'Success!';\n\t}\n\n.. note:: Not all database platforms support table repairs.\n\nOptimize a Database\n===================\n\nPermits you to optimize the database your DB class is currently\nconnected to. Returns an array containing the DB status messages or\nFALSE on failure.\n\n::\n\n\t$result = $this->dbutil->optimize_database();\n\n\tif ($result !== FALSE)\n\t{\n\t\tprint_r($result);\n\t}\n\n.. note:: Not all database platforms support database optimization. It\n\tit is mostly for use with MySQL.\n\nExport a Query Result as a CSV File\n===================================\n\nPermits you to generate a CSV file from a query result. The first\nparameter of the method must contain the result object from your\nquery. Example::\n\n\t$this->load->dbutil();\n\n\t$query = $this->db->query(\"SELECT * FROM mytable\");\n\n\techo $this->dbutil->csv_from_result($query);\n\nThe second, third, and fourth parameters allow you to set the delimiter\nnewline, and enclosure characters respectively. By default commas are\nused as the delimiter, \"\\n\" is used as a new line, and a double-quote\nis used as the enclosure. Example::\n\n\t$delimiter = \",\";\n\t$newline = \"\\r\\n\";\n\t$enclosure = '\"';\n\n\techo $this->dbutil->csv_from_result($query, $delimiter, $newline, $enclosure);\n\n.. important:: This method will NOT write the CSV file for you. It\n\tsimply creates the CSV layout. If you need to write the file\n\tuse the :doc:`File Helper <../helpers/file_helper>`.\n\nExport a Query Result as an XML Document\n========================================\n\nPermits you to generate an XML file from a query result. The first\nparameter expects a query result object, the second may contain an\noptional array of config parameters. Example::\n\n\t$this->load->dbutil();\n\n\t$query = $this->db->query(\"SELECT * FROM mytable\");\n\n\t$config = array (\n\t\t'root'\t\t=> 'root',\n\t\t'element'\t=> 'element',\n\t\t'newline'\t=> \"\\n\",\n\t\t'tab'\t\t=> \"\\t\"\n\t);\n\n\techo $this->dbutil->xml_from_result($query, $config);\n\n.. important:: This method will NOT write the XML file for you. It\n\tsimply creates the XML layout. If you need to write the file\n\tuse the :doc:`File Helper <../helpers/file_helper>`.\n\n********************\nBackup Your Database\n********************\n\nDatabase Backup Notes\n=====================\n\nPermits you to backup your full database or individual tables. The\nbackup data can be compressed in either Zip or Gzip format.\n\n.. note:: This feature is only available for MySQL and Interbase/Firebird databases.\n\n.. note:: For Interbase/Firebird databases, the backup file name is the only parameter.\n\n\t\t$this->dbutil->backup('db_backup_filename');\n\n.. note:: Due to the limited execution time and memory available to PHP,\n\tbacking up very large databases may not be possible. If your database is\n\tvery large you might need to backup directly from your SQL server via\n\tthe command line, or have your server admin do it for you if you do not\n\thave root privileges.\n\nUsage Example\n=============\n\n::\n\n\t// Load the DB utility class\n\t$this->load->dbutil();\n\n\t// Backup your entire database and assign it to a variable\n\t$backup = $this->dbutil->backup();\n\n\t// Load the file helper and write the file to your server\n\t$this->load->helper('file');\n\twrite_file('/path/to/mybackup.gz', $backup);\n\n\t// Load the download helper and send the file to your desktop\n\t$this->load->helper('download');\n\tforce_download('mybackup.gz', $backup);\n\nSetting Backup Preferences\n==========================\n\nBackup preferences are set by submitting an array of values to the first\nparameter of the ``backup()`` method. Example::\n\n\t$prefs = array(\n\t\t'tables'\t=> array('table1', 'table2'),\t// Array of tables to backup.\n\t\t'ignore'\t=> array(),\t\t\t// List of tables to omit from the backup\n\t\t'format'\t=> 'txt',\t\t\t// gzip, zip, txt\n\t\t'filename'\t=> 'mybackup.sql',\t\t// File name - NEEDED ONLY WITH ZIP FILES\n\t\t'add_drop'\t=> TRUE,\t\t\t// Whether to add DROP TABLE statements to backup file\n\t\t'add_insert'\t=> TRUE,\t\t\t// Whether to add INSERT data to backup file\n\t\t'newline'\t=> \"\\n\"\t\t\t\t// Newline character used in backup file\n\t);\n\n\t$this->dbutil->backup($prefs);\n\nDescription of Backup Preferences\n=================================\n\n======================= ======================= ======================= ========================================================================\nPreference              Default Value           Options                 Description\n======================= ======================= ======================= ========================================================================\n**tables**               empty array             None                    An array of tables you want backed up. If left blank all tables will be\n                                                                         exported.\n**ignore**               empty array             None                    An array of tables you want the backup routine to ignore.\n**format**               gzip                    gzip, zip, txt          The file format of the export file.\n**filename**             the current date/time   None                    The name of the backed-up file. The name is needed only if you are using\n                                                                         zip compression.\n**add_drop**             TRUE                    TRUE/FALSE              Whether to include DROP TABLE statements in your SQL export file.\n**add_insert**           TRUE                    TRUE/FALSE              Whether to include INSERT statements in your SQL export file.\n**newline**              \"\\\\n\"                   \"\\\\n\", \"\\\\r\", \"\\\\r\\\\n\"  Type of newline to use in your SQL export file.\n**foreign_key_checks**   TRUE                    TRUE/FALSE              Whether output should keep foreign key checks enabled.\n======================= ======================= ======================= ========================================================================\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_DB_utility\n\n\t.. php:method:: backup([$params = array()])\n\n\t\t:param\tarray\t$params: An associative array of options\n\t\t:returns:\traw/(g)zipped SQL query string\n\t\t:rtype:\tstring\n\n\t\tPerform a database backup, per user preferences.\n\n\t.. php:method:: database_exists($database_name)\n\n\t\t:param\tstring\t$database_name: Database name\n\t\t:returns:\tTRUE if the database exists, FALSE otherwise\n\t\t:rtype:\tbool\n\n\t\tCheck for the existence of a database.\n\n\t.. php:method:: list_databases()\n\n\t\t:returns:\tArray of database names found\n\t\t:rtype:\tarray\n\n\t\tRetrieve a list of all the database names.\n\n\t.. php:method:: optimize_database()\n\n\t\t:returns:\tArray of optimization messages or FALSE on failure\n\t\t:rtype:\tarray\n\n\t\tOptimizes the database.\n\n\t.. php:method:: optimize_table($table_name)\n\n\t\t:param\tstring\t$table_name:\tName of the table to optimize\n\t\t:returns:\tArray of optimization messages or FALSE on failure\n\t\t:rtype:\tarray\n\n\t\tOptimizes a database table.\n\n\t.. php:method:: repair_table($table_name)\n\n\t\t:param\tstring\t$table_name:\tName of the table to repair\n\t\t:returns:\tArray of repair messages or FALSE on failure\n\t\t:rtype:\tarray\n\n\t\tRepairs a database table.\n\n\t.. php:method:: csv_from_result($query[, $delim = ','[, $newline = \"\\n\"[, $enclosure = '\"']]])\n\n\t\t:param\tobject\t$query:\tA database result object\n\t\t:param\tstring\t$delim: The CSV field delimiter to use\n\t\t:param\tstring\t$newline: The newline character to use\n\t\t:param\tstring\t$enclosure: The enclosure delimiter to use\n\t\t:returns:\tThe generated CSV file as a string\n\t\t:rtype:\tstring\n\n\t\tTranslates a database result object into a CSV document.\n\n\t.. php:method:: xml_from_result($query[, $params = array()])\n\n\t\t:param\tobject\t$query: A database result object\n\t\t:param\tarray\t$params: An associative array of preferences\n\t\t:returns:\tThe generated XML document as a string\n\t\t:rtype:\tstring\n\n\t\tTranslates a database result object into an XML document.\n"
  },
  {
    "path": "user_guide_src/source/documentation/index.rst",
    "content": "#################################\nWriting CodeIgniter Documentation\n#################################\n\nCodeIgniter uses Sphinx to generate its documentation in a variety of formats,\nusing reStructuredText to handle the formatting.  If you are familiar with\nMarkdown or Textile, you will quickly grasp reStructuredText.  The focus is\non readability and user friendliness.\nWhile they can be quite technical, we always write for humans!\n\nA local table of contents should always be included, like the one below.\nIt is created automatically by inserting the following:\n\n::\n\n\t.. contents::\n\t\t:local:\n\n\t.. raw:: html\n\n\t<div class=\"custom-index container\"></div>\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\nThe <div> that is inserted as raw HTML is a hook for the documentation's\nJavaScript to dynamically add links to any function and method definitions\ncontained in the current page.\n\n**************\nTools Required\n**************\n\nTo see the rendered HTML, ePub, PDF, etc., you will need to install Sphinx\nalong with the PHP domain extension for Sphinx.  The underlying requirement\nis to have Python installed.  Lastly, you will install the CI Lexer for\nPygments, so that code blocks can be properly highlighted.\n\n.. code-block:: bash\n\n\teasy_install \"sphinx==1.2.3\"\n\teasy_install \"sphinxcontrib-phpdomain==0.1.3.post1\"\n\nThen follow the directions in the README file in the :samp:`cilexer` folder\ninside the documentation repository to install the CI Lexer.\n\n\n\n*****************************************\nPage and Section Headings and Subheadings\n*****************************************\n\nHeadings not only provide order and sections within a page, but they also\nare used to automatically build both the page and document table of contents.\nHeadings are formed by using certain characters as underlines for a bit of\ntext.  Major headings, like page titles and section headings also use\noverlines.  Other headings just use underlines, with the following hierarchy::\n\n\t# with overline for page titles\n\t* with overline for major sections\n\t= for subsections\n\t- for subsubsections\n\t^ for subsubsubsections\n\t\" for subsubsubsubsections (!)\n\nThe :download:`TextMate ELDocs Bundle <./ELDocs.tmbundle.zip>` can help you\ncreate these with the following tab triggers::\n\n\ttitle->\n\n\t\t##########\n\t\tPage Title\n\t\t##########\n\n\tsec->\n\n\t\t*************\n\t\tMajor Section\n\t\t*************\n\n\tsub->\n\n\t\tSubsection\n\t\t==========\n\n\tsss->\n\n\t\tSubSubSection\n\t\t-------------\n\n\tssss->\n\n\t\tSubSubSubSection\n\t\t^^^^^^^^^^^^^^^^\n\n\tsssss->\n\n\t\tSubSubSubSubSection (!)\n\t\t\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\n\n\n\n********************\nMethod Documentation\n********************\n\nWhen documenting class methods for third party developers, Sphinx provides\ndirectives to assist and keep things simple.  \nFor example, consider the following ReST:\n\n.. code-block:: rst\n\n\t.. php:class:: Some_class\n\n\t\t.. php:method:: some_method ( $foo [, $bar [, $bat]])\n\n\t\t\tThis function will perform some action. The ``$bar`` array must contain\n\t\t\ta something and something else, and along with ``$bat`` is an optional\n\t\t\tparameter.\n\n\t\t\t:param int $foo: the foo id to do something in\n\t\t\t:param mixed $bar: A data array that must contain a something and something else\n\t\t\t:param bool $bat: whether or not to do something\n\t\t\t:returns: FALSE on failure, TRUE if successful\n\t\t\t:rtype: bool\n\n\t\t\t::\n\n\t\t\t\t$this->load->library('some_class');\n\n\t\t\t\t$bar = array(\n\t\t\t\t\t'something'\t\t=> 'Here is this parameter!',\n\t\t\t\t\t'something_else'\t=> 42\n\t\t\t\t);\n\n\t\t\t\t$bat = $this->some_class->should_do_something();\n\n\t\t\t\tif ($this->some_class->some_method(4, $bar, $bat) === FALSE)\n\t\t\t\t{\n\t\t\t\t\tshow_error('An Error Occurred Doing Some Method');\n\t\t\t\t}\n\n\t\t\t.. note:: Here is something that you should be aware of when using some_method().\n\t\t\t\t\tFor real.\n\n\t\t\tSee also :meth:`Some_class::should_do_something`\n\n\n\t\t.. php:method:: should_do_something()\n\n\t\t\t:returns: Whether or not something should be done\n\t\t\t:rtype: bool\n\n\nIt creates the following display:\n\n.. php:class:: Some_class\n\n\n\t.. php:method:: some_method ( $foo [, $bar [, $bat]])\n\n\t\tThis function will perform some action. The ``$bar`` array must contain\n\t\ta something and something else, and along with ``$bat`` is an optional\n\t\tparameter.\n\n\t\t:param int $foo: the foo id to do something in\n\t\t:param mixed $bar: A data array that must contain a something and something else\n\t\t:param bool $bat: whether or not to do something\n\t\t:returns: FALSE on failure, TRUE if successful\n\t\t:rtype: bool\n\n\t\t::\n\n\t\t\t$this->load->library('some_class');\n\n\t\t\t$bar = array(\n\t\t\t\t'something'\t\t=> 'Here is this parameter!',\n\t\t\t\t'something_else'\t=> 42\n\t\t\t);\n\n\t\t\t$bat = $this->some_class->should_do_something();\n\n\t\t\tif ($this->some_class->some_method(4, $bar, $bat) === FALSE)\n\t\t\t{\n\t\t\t\tshow_error('An Error Occurred Doing Some Method');\n\t\t\t}\n\n\t\t.. note:: Here is something that you should be aware of when using some_method().\n\t\t\t\tFor real.\n\n\t\tSee also :meth:`Some_class::should_do_something`\n\n\n\t.. php:method:: should_do_something()\n\n\t\t:returns: Whether or not something should be done\n\t\t:rtype: bool\n"
  },
  {
    "path": "user_guide_src/source/general/alternative_php.rst",
    "content": "###################################\nAlternate PHP Syntax for View Files\n###################################\n\nIf you do not utilize CodeIgniter's :doc:`template\nengine <../libraries/parser>`, you'll be using pure PHP in your\nView files. To minimize the PHP code in these files, and to make it\neasier to identify the code blocks it is recommended that you use PHPs\nalternative syntax for control structures and short tag echo statements.\nIf you are not familiar with this syntax, it allows you to eliminate the\nbraces from your code, and eliminate \"echo\" statements.\n\nAlternative Echos\n=================\n\nNormally to echo, or print out a variable you would do this::\n\n\t<?php echo $variable; ?>\n\nWith the alternative syntax you can instead do it this way::\n\n\t<?=$variable?>\n\nAlternative Control Structures\n==============================\n\nControls structures, like if, for, foreach, and while can be written in\na simplified format as well. Here is an example using ``foreach``::\n\n\t<ul>\n\n\t<?php foreach ($todo as $item): ?>\n\n\t\t<li><?=$item?></li>\n\n\t<?php endforeach; ?>\n\n\t</ul>\n\nNotice that there are no braces. Instead, the end brace is replaced with\n``endforeach``. Each of the control structures listed above has a similar\nclosing syntax: ``endif``, ``endfor``, ``endforeach``, and ``endwhile``\n\nAlso notice that instead of using a semicolon after each structure\n(except the last one), there is a colon. This is important!\n\nHere is another example, using ``if``/``elseif``/``else``. Notice the colons::\n\n\t<?php if ($username === 'sally'): ?>\n\n\t\t<h3>Hi Sally</h3>\n\n\t<?php elseif ($username === 'joe'): ?>\n\n\t\t<h3>Hi Joe</h3>\n\n\t<?php else: ?>\n\n\t\t<h3>Hi unknown user</h3>\n\n\t<?php endif; ?>\n"
  },
  {
    "path": "user_guide_src/source/general/ancillary_classes.rst",
    "content": "##########################\nCreating Ancillary Classes\n##########################\n\nIn some cases you may want to develop classes that exist apart from your\ncontrollers but have the ability to utilize all of CodeIgniter's\nresources. This is easily possible as you'll see.\n\nget_instance()\n==============\n\n.. php:function:: get_instance()\n\n\t:returns:\tReference to your controller's instance\n\t:rtype:\tCI_Controller\n\n**Any class that you instantiate within your controller methods can\naccess CodeIgniter's native resources** simply by using the\n``get_instance()`` function. This function returns the main\nCodeIgniter object.\n\nNormally, to call any of the available methods, CodeIgniter requires\nyou to use the ``$this`` construct::\n\n\t$this->load->helper('url');\n\t$this->load->library('session');\n\t$this->config->item('base_url');\n\t// etc.\n\n``$this``, however, only works within your controllers, your models,\nor your views. If you would like to use CodeIgniter's classes from\nwithin your own custom classes you can do so as follows:\n\nFirst, assign the CodeIgniter object to a variable::\n\n\t$CI =& get_instance();\n\nOnce you've assigned the object to a variable, you'll use that variable\n*instead* of ``$this``::\n\n\t$CI =& get_instance();\n\n\t$CI->load->helper('url');\n\t$CI->load->library('session');\n\t$CI->config->item('base_url');\n\t// etc.\n\nIf you'll be using ``get_instance()`` inside another class, then it would\nbe better if you assign it to a property. This way, you won't need to call\n``get_instance()`` in every single method.\n\nExample::\n\n\tclass Example {\n\n\t\tprotected $CI;\n\n\t\t// We'll use a constructor, as you can't directly call a function\n\t\t// from a property definition.\n\t\tpublic function __construct()\n\t\t{\n\t\t\t// Assign the CodeIgniter super-object\n\t\t\t$this->CI =& get_instance();\n\t\t}\n\n\t\tpublic function foo()\n\t\t{\n\t\t\t$this->CI->load->helper('url');\n\t\t\tredirect();\n\t\t}\n\n\t\tpublic function bar()\n\t\t{\n\t\t\t$this->CI->config->item('base_url');\n\t\t}\n\t}\n\nIn the above example, both methods ``foo()`` and ``bar()`` will work\nafter you instantiate the Example class, without the need to call\n``get_instance()`` in each of them.\n"
  },
  {
    "path": "user_guide_src/source/general/autoloader.rst",
    "content": "######################\nAuto-loading Resources\n######################\n\nCodeIgniter comes with an \"Auto-load\" feature that permits libraries,\nhelpers, and models to be initialized automatically every time the\nsystem runs. If you need certain resources globally throughout your\napplication you should consider auto-loading them for convenience.\n\nThe following items can be loaded automatically:\n\n-  Classes found in the *libraries/* directory\n-  Helper files found in the *helpers/* directory\n-  Custom config files found in the *config/* directory\n-  Language files found in the *system/language/* directory\n-  Models found in the *models/* folder\n\nTo autoload resources, open the **application/config/autoload.php**\nfile and add the item you want loaded to the autoload array. You'll\nfind instructions in that file corresponding to each type of item.\n\n.. note:: Do not include the file extension (.php) when adding items to\n\tthe autoload array.\n\nAdditionally, if you want CodeIgniter to use a `Composer <https://getcomposer.org/>`_\nauto-loader, just set ``$config['composer_autoload']`` to ``TRUE`` or\na custom path in **application/config/config.php**."
  },
  {
    "path": "user_guide_src/source/general/caching.rst",
    "content": "################\nWeb Page Caching\n################\n\nCodeIgniter lets you cache your pages in order to achieve maximum\nperformance.\n\nAlthough CodeIgniter is quite fast, the amount of dynamic information\nyou display in your pages will correlate directly to the server\nresources, memory, and processing cycles utilized, which affect your\npage load speeds. By caching your pages, since they are saved in their\nfully rendered state, you can achieve performance that nears that of\nstatic web pages.\n\nHow Does Caching Work?\n======================\n\nCaching can be enabled on a per-page basis, and you can set the length\nof time that a page should remain cached before being refreshed. When a\npage is loaded for the first time, the cache file will be written to\nyour application/cache folder. On subsequent page loads the cache file\nwill be retrieved and sent to the requesting user's browser. If it has\nexpired, it will be deleted and refreshed before being sent to the\nbrowser.\n\n.. note: The Benchmark tag is not cached so you can still view your page\n\tload speed when caching is enabled.\n\nEnabling Caching\n================\n\nTo enable caching, put the following tag in any of your controller\nmethods::\n\n\t$this->output->cache($n);\n\nWhere ``$n`` is the number of **minutes** you wish the page to remain\ncached between refreshes.\n\nThe above tag can go anywhere within a method. It is not affected by\nthe order that it appears, so place it wherever it seems most logical to\nyou. Once the tag is in place, your pages will begin being cached.\n\n.. important:: Because of the way CodeIgniter stores content for output,\n\tcaching will only work if you are generating display for your\n\tcontroller with a :doc:`view <./views>`.\n\n.. important:: If you change configuration options that might affect\n\tyour output, you have to manually delete your cache files.\n\n.. note:: Before the cache files can be written you must set the file\n\tpermissions on your *application/cache/* directory such that\n\tit is writable.\n\nDeleting Caches\n===============\n\nIf you no longer wish to cache a file you can remove the caching tag and\nit will no longer be refreshed when it expires.\n\n.. note:: Removing the tag will not delete the cache immediately. It will\n\thave to expire normally.\n\nIf you need to manually delete the cache, you can use the ``delete_cache()``\nmethod::\n\n\t// Deletes cache for the currently requested URI\n\t$this->output->delete_cache();\n\n\t// Deletes cache for /foo/bar\n\t$this->output->delete_cache('/foo/bar');"
  },
  {
    "path": "user_guide_src/source/general/cli.rst",
    "content": "###################\nRunning via the CLI\n###################\n\nAs well as calling an applications :doc:`Controllers <./controllers>`\nvia the URL in a browser they can also be loaded via the command-line\ninterface (CLI).\n\n.. contents:: Page Contents\n\nWhat is the CLI?\n================\n\nThe command-line interface is a text-based method of interacting with\ncomputers. For more information, check the `Wikipedia\narticle <https://en.wikipedia.org/wiki/Command-line_interface>`_.\n\nWhy run via the command-line?\n=============================\n\nThere are many reasons for running CodeIgniter from the command-line,\nbut they are not always obvious.\n\n-  Run your cron-jobs without needing to use *wget* or *curl*\n-  Make your cron-jobs inaccessible from being loaded in the URL by\n   checking the return value of :php:func:`is_cli()`.\n-  Make interactive \"tasks\" that can do things like set permissions,\n   prune cache folders, run backups, etc.\n-  Integrate with other applications in other languages. For example, a\n   random C++ script could call one command and run code in your models!\n\nLet's try it: Hello World!\n==========================\n\nLet's create a simple controller so you can see it in action. Using your\ntext editor, create a file called Tools.php, and put the following code\nin it::\n\n\t<?php\n\tclass Tools extends CI_Controller {\n\n\t\tpublic function message($to = 'World')\n\t\t{\n\t\t\techo \"Hello {$to}!\".PHP_EOL;\n\t\t}\n\t}\n\nThen save the file to your *application/controllers/* folder.\n\nNow normally you would visit the site using a URL similar to this::\n\n\texample.com/index.php/tools/message/to\n\nInstead, we are going to open the terminal in Mac/Linux or go to Run > \"cmd\"\nin Windows and navigate to our CodeIgniter project.\n\n.. code-block:: bash\n\n\t$ cd /path/to/project;\n\t$ php index.php tools message\n\nIf you did it right, you should see *Hello World!* printed.\n\n.. code-block:: bash\n\n\t$ php index.php tools message \"John Smith\"\n\nHere we are passing it an argument in the same way that URL parameters\nwork. \"John Smith\" is passed as an argument and the output is::\n\n\tHello John Smith!\n\nThat's it!\n==========\n\nThat, in a nutshell, is all there is to know about controllers on the\ncommand line. Remember that this is just a normal controller, so routing\nand ``_remap()`` works fine.\n"
  },
  {
    "path": "user_guide_src/source/general/common_functions.rst",
    "content": "################\nCommon Functions\n################\n\nCodeIgniter uses a few functions for its operation that are globally\ndefined, and are available to you at any point. These do not require\nloading any libraries or helpers.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n.. php:function:: is_php($version)\n\n\t:param\tstring\t$version: Version number\n\t:returns:\tTRUE if the running PHP version is at least the one specified or FALSE if not\n\t:rtype:\tbool\n\n\tDetermines if the PHP version being used is greater than the\n\tsupplied version number.\n\n\tExample::\n\n\t\tif (is_php('5.5'))\n\t\t{\n\t\t\techo json_last_error_msg();\n\t\t}\n\n\tReturns boolean TRUE if the installed version of PHP is equal to or\n\tgreater than the supplied version number. Returns FALSE if the installed\n\tversion of PHP is lower than the supplied version number.\n\n.. php:function:: is_really_writable($file)\n\n\t:param\tstring\t$file: File path\n\t:returns:\tTRUE if the path is writable, FALSE if not\n\t:rtype:\tbool\n\n\t``is_writable()`` returns TRUE on Windows servers when you really can't\n\twrite to the file as the OS reports to PHP as FALSE only if the\n\tread-only attribute is marked.\n\n\tThis function determines if a file is actually writable by attempting\n\tto write to it first. Generally only recommended on platforms where\n\tthis information may be unreliable.\n\n\tExample::\n\n\t\tif (is_really_writable('file.txt'))\n\t\t{\n\t\t\techo \"I could write to this if I wanted to\";\n\t\t}\n\t\telse\n\t\t{\n\t\t\techo \"File is not writable\";\n\t\t}\n\n\t.. note:: See also `PHP bug #54709 <https://bugs.php.net/bug.php?id=54709>`_ for more info.\n\n.. php:function:: config_item($key)\n\n\t:param\tstring\t$key: Config item key\n\t:returns:\tConfiguration key value or NULL if not found\n\t:rtype:\tmixed\n\n\tThe :doc:`Config Library <../libraries/config>` is the preferred way of\n\taccessing configuration information, however ``config_item()`` can be used\n\tto retrieve single keys. See :doc:`Config Library <../libraries/config>`\n\tdocumentation for more information.\n\n.. :noindex: function:: show_error($message, $status_code[, $heading = 'An Error Was Encountered'])\n\n\t:param\tmixed\t$message: Error message\n\t:param\tint\t$status_code: HTTP Response status code\n\t:param\tstring\t$heading: Error page heading\n\t:rtype:\tvoid\n\n\tThis function calls ``CI_Exception::show_error()``. For more info,\n\tplease see the :doc:`Error Handling <errors>` documentation.\n\n.. :noindex: function:: show_404([$page = ''[, $log_error = TRUE]])\n\n\t:param\tstring\t$page: URI string\n\t:param\tbool\t$log_error: Whether to log the error\n\t:rtype:\tvoid\n\n\tThis function calls ``CI_Exception::show_404()``. For more info,\n\tplease see the :doc:`Error Handling <errors>` documentation.\n\n.. :noindex: function:: log_message($level, $message)\n\n\t:param\tstring\t$level: Log level: 'error', 'debug' or 'info'\n\t:param\tstring\t$message: Message to log\n\t:rtype:\tvoid\n\n\tThis function is an alias for ``CI_Log::write_log()``. For more info,\n\tplease see the :doc:`Error Handling <errors>` documentation.\n\n.. php:function:: set_status_header($code[, $text = ''])\n\n\t:param\tint\t$code: HTTP Response status code\n\t:param\tstring\t$text: A custom message to set with the status code\n\t:rtype:\tvoid\n\n\tPermits you to manually set a server status header. Example::\n\n\t\tset_status_header(401);\n\t\t// Sets the header as:  Unauthorized\n\n\t`See here <https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html>`_ for\n\ta full list of headers.\n\n.. php:function:: remove_invisible_characters($str[, $url_encoded = TRUE])\n\n\t:param\tstring\t$str: Input string\n\t:param\tbool\t$url_encoded: Whether to remove URL-encoded characters as well\n\t:returns:\tSanitized string\n\t:rtype:\tstring\n\n\tThis function prevents inserting NULL characters between ASCII\n\tcharacters, like Java\\\\0script.\n\n\tExample::\n\n\t\tremove_invisible_characters('Java\\\\0script');\n\t\t// Returns: 'Javascript'\n\n.. php:function:: html_escape($var)\n\n\t:param\tmixed\t$var: Variable to escape (string or array)\n\t:returns:\tHTML escaped string(s)\n\t:rtype:\tmixed\n\n\tThis function acts as an alias for PHP's native ``htmlspecialchars()``\n\tfunction, with the advantage of being able to accept an array of strings.\n\n\tIt is useful in preventing Cross Site Scripting (XSS).\n\n.. php:function:: get_mimes()\n\n\t:returns:\tAn associative array of file types\n\t:rtype:\tarray\n\n\tThis function returns a *reference* to the MIMEs array from\n\t*application/config/mimes.php*.\n\n.. php:function:: is_https()\n\n\t:returns:\tTRUE if currently using HTTP-over-SSL, FALSE if not\n\t:rtype:\tbool\n\n\tReturns TRUE if a secure (HTTPS) connection is used and FALSE\n\tin any other case (including non-HTTP requests).\n\n.. php:function:: is_cli()\n\n\t:returns:\tTRUE if currently running under CLI, FALSE otherwise\n\t:rtype:\tbool\n\n\tReturns TRUE if the application is run through the command line\n\tand FALSE if not.\n\n\t.. note:: This function checks both if the ``PHP_SAPI`` value is 'cli'\n\t\tor if the ``STDIN`` constant is defined.\n\n.. php:function:: function_usable($function_name)\n\n\t:param\tstring\t$function_name: Function name\n\t:returns:\tTRUE if the function can be used, FALSE if not\n\t:rtype:\tbool\n\n\tReturns TRUE if a function exists and is usable, FALSE otherwise.\n\n\tThis function runs a ``function_exists()`` check and if the\n\t`Suhosin extension <http://www.hardened-php.net/suhosin/>` is loaded,\n\tchecks if it doesn't disable the function being checked.\n\n\tIt is useful if you want to check for the availability of functions\n\tsuch as ``eval()`` and ``exec()``, which are dangerous and might be\n\tdisabled on servers with highly restrictive security policies.\n\n\t.. note:: This function was introduced because Suhosin terminated\n\t\tscript execution, but this turned out to be a bug. A fix\n\t\thas been available for some time (version 0.9.34), but is\n\t\tunfortunately not released yet.\n"
  },
  {
    "path": "user_guide_src/source/general/compatibility_functions.rst",
    "content": "#######################\nCompatibility Functions\n#######################\n\nCodeIgniter provides a set of compatibility functions that enable\nyou to use functions what are otherwise natively available in PHP,\nbut only in higher versions or depending on a certain extension.\n\nBeing custom implementations, these functions will also have some\nset of dependencies on their own, but are still useful if your\nPHP setup doesn't offer them natively.\n\n.. note:: Much like the :doc:`common functions <common_functions>`, the\n\tcompatibility functions are always available, as long as\n\ttheir dependencies are met.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n****************\nPassword Hashing\n****************\n\nThis set of compatibility functions offers a \"backport\" of PHP's\nstandard `Password Hashing extension <https://secure.php.net/password>`_\nthat is otherwise available only since PHP 5.5.\n\nDependencies\n============\n\n- ``CRYPT_BLOWFISH`` support for ``crypt()``\n\nConstants\n=========\n\n- ``PASSWORD_BCRYPT``\n- ``PASSWORD_DEFAULT``\n\nFunction reference\n==================\n\n.. php:function:: password_get_info($hash)\n\n\t:param\tstring\t$hash: Password hash\n\t:returns:\tInformation about the hashed password\n\t:rtype:\tarray\n\n\tFor more information, please refer to the `PHP manual for\n\tpassword_get_info() <https://secure.php.net/password_get_info>`_.\n\n.. php:function:: password_hash($password, $algo[, $options = array()])\n\n\t:param\tstring\t$password: Plain-text password\n\t:param\tint\t$algo: Hashing algorithm\n\t:param\tarray\t$options: Hashing options\n\t:returns:\tHashed password or FALSE on failure\n\t:rtype:\tstring\n\n\tFor more information, please refer to the `PHP manual for\n\tpassword_hash() <https://secure.php.net/password_hash>`_.\n\n\t.. note:: Unless you provide your own (and valid) salt, this function\n\t\thas a further dependency on an available CSPRNG source. Each\n\t\tof the following would satisfy that:\n\t\t- ``mcrypt_create_iv()`` with ``MCRYPT_DEV_URANDOM``\n\t\t- ``openssl_random_pseudo_bytes()``\n\t\t- /dev/arandom\n\t\t- /dev/urandom\n\n.. php:function:: password_needs_rehash()\n\n\t:param\tstring\t$hash: Password hash\n\t:param\tint\t$algo: Hashing algorithm\n\t:param\tarray\t$options: Hashing options\n\t:returns:\tTRUE if the hash should be rehashed to match the given algorithm and options, FALSE otherwise\n\t:rtype:\tbool\n\n\tFor more information, please refer to the `PHP manual for\n\tpassword_needs_rehash() <https://secure.php.net/password_needs_rehash>`_.\n\n.. php:function:: password_verify($password, $hash)\n\n\t:param\tstring\t$password: Plain-text password\n\t:param\tstring\t$hash: Password hash\n\t:returns:\tTRUE if the password matches the hash, FALSE if not\n\t:rtype:\tbool\n\n\tFor more information, please refer to the `PHP manual for\n\tpassword_verify() <https://secure.php.net/password_verify>`_.\n\n*********************\nHash (Message Digest)\n*********************\n\nThis compatibility layer contains backports for the ``hash_equals()``\nand ``hash_pbkdf2()`` functions, which otherwise require PHP 5.6 and/or\nPHP 5.5 respectively.\n\nDependencies\n============\n\n- None\n\nFunction reference\n==================\n\n.. php:function:: hash_equals($known_string, $user_string)\n\n\t:param\tstring\t$known_string: Known string\n\t:param\tstring\t$user_string: User-supplied string\n\t:returns:\tTRUE if the strings match, FALSE otherwise\n\t:rtype:\tstring\n\n\tFor more information, please refer to the `PHP manual for\n\thash_equals() <https://secure.php.net/hash_equals>`_.\n\n.. php:function:: hash_pbkdf2($algo, $password, $salt, $iterations[, $length = 0[, $raw_output = FALSE]])\n\n\t:param\tstring\t$algo: Hashing algorithm\n\t:param\tstring\t$password: Password\n\t:param\tstring\t$salt: Hash salt\n\t:param\tint\t$iterations: Number of iterations to perform during derivation\n\t:param\tint\t$length: Output string length\n\t:param\tbool\t$raw_output: Whether to return raw binary data\n\t:returns:\tPassword-derived key or FALSE on failure\n\t:rtype:\tstring\n\n\tFor more information, please refer to the `PHP manual for\n\thash_pbkdf2() <https://secure.php.net/hash_pbkdf2>`_.\n\n****************\nMultibyte String\n****************\n\nThis set of compatibility functions offers limited support for PHP's\n`Multibyte String extension <https://secure.php.net/mbstring>`_. Because of\nthe limited alternative solutions, only a few functions are available.\n\n.. note:: When a character set parameter is ommited,\n\t``$config['charset']`` will be used.\n\nDependencies\n============\n\n- `iconv <https://secure.php.net/iconv>`_ extension\n\n.. important:: This dependency is optional and these functions will\n\talways be declared. If iconv is not available, they WILL\n\tfall-back to their non-mbstring versions.\n\n.. important:: Where a character set is supplied, it must be\n\tsupported by iconv and in a format that it recognizes.\n\n.. note:: For you own dependency check on the actual mbstring\n\textension, use the ``MB_ENABLED`` constant.\n\nFunction reference\n==================\n\n.. php:function:: mb_strlen($str[, $encoding = NULL])\n\n\t:param\tstring\t$str: Input string\n\t:param\tstring\t$encoding: Character set\n\t:returns:\tNumber of characters in the input string or FALSE on failure\n\t:rtype:\tstring\n\n\tFor more information, please refer to the `PHP manual for\n\tmb_strlen() <https://secure.php.net/mb_strlen>`_.\n\n.. php:function:: mb_strpos($haystack, $needle[, $offset = 0[, $encoding = NULL]])\n\n\t:param\tstring\t$haystack: String to search in\n\t:param\tstring\t$needle: Part of string to search for\n\t:param\tint\t$offset: Search offset\n\t:param\tstring\t$encoding: Character set\n\t:returns:\tNumeric character position of where $needle was found or FALSE if not found\n\t:rtype:\tmixed\n\n\tFor more information, please refer to the `PHP manual for\n\tmb_strpos() <https://secure.php.net/mb_strpos>`_.\n\n.. php:function:: mb_substr($str, $start[, $length = NULL[, $encoding = NULL]])\n\n\t:param\tstring\t$str: Input string\n\t:param\tint\t$start: Position of first character\n\t:param\tint\t$length: Maximum number of characters\n\t:param\tstring\t$encoding: Character set\n\t:returns:\tPortion of $str specified by $start and $length or FALSE on failure\n\t:rtype:\tstring\n\n\tFor more information, please refer to the `PHP manual for\n\tmb_substr() <https://secure.php.net/mb_substr>`_.\n\n******************\nStandard Functions\n******************\n\nThis set of compatibility functions offers support for a few\nstandard functions in PHP that otherwise require a newer PHP version.\n\nDependencies\n============\n\n- None\n\nFunction reference\n==================\n\n.. php:function:: array_column(array $array, $column_key[, $index_key = NULL])\n\n\t:param\tarray\t$array: Array to fetch results from\n\t:param\tmixed\t$column_key: Key of the column to return values from\n\t:param\tmixed\t$index_key: Key to use for the returned values\n\t:returns:\tAn array of values representing a single column from the input array\n\t:rtype:\tarray\n\n\tFor more information, please refer to the `PHP manual for\n\tarray_column() <https://secure.php.net/array_column>`_.\n"
  },
  {
    "path": "user_guide_src/source/general/controllers.rst",
    "content": "###########\nControllers\n###########\n\nControllers are the heart of your application, as they determine how\nHTTP requests should be handled.\n\n.. contents:: Page Contents\n\nWhat is a Controller?\n=====================\n\n**A Controller is simply a class file that is named in a way that can be\nassociated with a URI.**\n\nConsider this URI::\n\n\texample.com/index.php/blog/\n\nIn the above example, CodeIgniter would attempt to find a controller\nnamed Blog.php and load it.\n\n**When a controller's name matches the first segment of a URI, it will\nbe loaded.**\n\nLet's try it: Hello World!\n==========================\n\nLet's create a simple controller so you can see it in action. Using your\ntext editor, create a file called Blog.php, and put the following code\nin it::\n\n\t<?php\n\tclass Blog extends CI_Controller {\n\n\t\tpublic function index()\n\t\t{\n\t\t\techo 'Hello World!';\n\t\t}\n\t}\n\nThen save the file to your *application/controllers/* directory.\n\n.. important:: The file must be called 'Blog.php', with a capital 'B'.\n\nNow visit the your site using a URL similar to this::\n\n\texample.com/index.php/blog/\n\nIf you did it right, you should see:\n\n\tHello World!\n\n.. important:: Class names must start with an uppercase letter.\n\nThis is valid::\n\n\t<?php\n\tclass Blog extends CI_Controller {\n\n\t}\n\t\nThis is **not** valid::\n\n\t<?php\n\tclass blog extends CI_Controller {\n\n\t}\n\nAlso, always make sure your controller extends the parent controller\nclass so that it can inherit all its methods.\n\nMethods\n=======\n\nIn the above example the method name is ``index()``. The \"index\" method\nis always loaded by default if the **second segment** of the URI is\nempty. Another way to show your \"Hello World\" message would be this::\n\n\texample.com/index.php/blog/index/\n\n**The second segment of the URI determines which method in the\ncontroller gets called.**\n\nLet's try it. Add a new method to your controller::\n\n\t<?php\n\tclass Blog extends CI_Controller {\n\n\t\tpublic function index()\n\t\t{\n\t\t\techo 'Hello World!';\n\t\t}\n\n\t\tpublic function comments()\n\t\t{\n\t\t\techo 'Look at this!';\n\t\t}\n\t}\n\nNow load the following URL to see the comment method::\n\n\texample.com/index.php/blog/comments/\n\nYou should see your new message.\n\nPassing URI Segments to your methods\n====================================\n\nIf your URI contains more than two segments they will be passed to your\nmethod as parameters.\n\nFor example, let's say you have a URI like this::\n\n\texample.com/index.php/products/shoes/sandals/123\n\nYour method will be passed URI segments 3 and 4 (\"sandals\" and \"123\")::\n\n\t<?php\n\tclass Products extends CI_Controller {\n\n\t\tpublic function shoes($sandals, $id)\n\t\t{\n\t\t\techo $sandals;\n\t\t\techo $id;\n\t\t}\n\t}\n\n.. important:: If you are using the :doc:`URI Routing <routing>`\n\tfeature, the segments passed to your method will be the re-routed\n\tones.\n\nDefining a Default Controller\n=============================\n\nCodeIgniter can be told to load a default controller when a URI is not\npresent, as will be the case when only your site root URL is requested.\nTo specify a default controller, open your **application/config/routes.php**\nfile and set this variable::\n\n\t$route['default_controller'] = 'blog';\n\nWhere 'blog' is the name of the controller class you want used. If you now\nload your main index.php file without specifying any URI segments you'll\nsee your \"Hello World\" message by default.\n\nFor more information, please refer to the \"Reserved Routes\" section of the\n:doc:`URI Routing <routing>` documentation.\n\nRemapping Method Calls\n======================\n\nAs noted above, the second segment of the URI typically determines which\nmethod in the controller gets called. CodeIgniter permits you to override\nthis behavior through the use of the ``_remap()`` method::\n\n\tpublic function _remap()\n\t{\n\t\t// Some code here...\n\t}\n\n.. important:: If your controller contains a method named _remap(),\n\tit will **always** get called regardless of what your URI contains. It\n\toverrides the normal behavior in which the URI determines which method\n\tis called, allowing you to define your own method routing rules.\n\nThe overridden method call (typically the second segment of the URI) will\nbe passed as a parameter to the ``_remap()`` method::\n\n\tpublic function _remap($method)\n\t{\n\t\tif ($method === 'some_method')\n\t\t{\n\t\t\t$this->$method();\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$this->default_method();\n\t\t}\n\t}\n\nAny extra segments after the method name are passed into ``_remap()`` as an\noptional second parameter. This array can be used in combination with\nPHP's `call_user_func_array() <https://secure.php.net/call_user_func_array>`_\nto emulate CodeIgniter's default behavior.\n\nExample::\n\n\tpublic function _remap($method, $params = array())\n\t{\n\t\t$method = 'process_'.$method;\n\t\tif (method_exists($this, $method))\n\t\t{\n\t\t\treturn call_user_func_array(array($this, $method), $params);\n\t\t}\n\t\tshow_404();\n\t}\n\nProcessing Output\n=================\n\nCodeIgniter has an output class that takes care of sending your final\nrendered data to the web browser automatically. More information on this\ncan be found in the :doc:`Views <views>` and :doc:`Output Class\n<../libraries/output>` pages. In some cases, however, you might want to\npost-process the finalized data in some way and send it to the browser\nyourself. CodeIgniter permits you to add a method named ``_output()``\nto your controller that will receive the finalized output data.\n\n.. important:: If your controller contains a method named ``_output()``,\n\tit will **always** be called by the output class instead of\n\techoing the finalized data directly. The first parameter of the\n\tmethod will contain the finalized output.\n\nHere is an example::\n\n\tpublic function _output($output)\n\t{\n\t\techo $output;\n\t}\n\n.. note::\n\n\tPlease note that your ``_output()`` method will receive the\n\tdata in its finalized state. Benchmark and memory usage data\n\twill be rendered, cache files written (if you have caching\n\tenabled), and headers will be sent (if you use that\n\t:doc:`feature <../libraries/output>`) before it is handed off\n\tto the ``_output()`` method.\n\tTo have your controller's output cached properly, its\n\t``_output()`` method can use::\n\n\t\tif ($this->output->cache_expiration > 0)\n\t\t{\n\t\t\t$this->output->_write_cache($output);\n\t\t}\n\n\tIf you are using this feature the page execution timer and\n\tmemory usage stats might not be perfectly accurate since they\n\twill not take into account any further processing you do.\n\tFor an alternate way to control output *before* any of the\n\tfinal processing is done, please see the available methods\n\tin the :doc:`Output Library <../libraries/output>`.\n\nPrivate methods\n===============\n\nIn some cases you may want certain methods hidden from public access.\nIn order to achieve this, simply declare the method as being private\nor protected and it will not be served via a URL request. For example,\nif you were to have a method like this::\n\n\tprivate function _utility()\n\t{\n\t\t// some code\n\t}\n\nTrying to access it via the URL, like this, will not work::\n\n\texample.com/index.php/blog/_utility/\n\n.. note:: Prefixing method names with an underscore will also prevent\n\tthem from being called. This is a legacy feature that is left\n\tfor backwards-compatibility.\n\nOrganizing Your Controllers into Sub-directories\n================================================\n\nIf you are building a large application you might want to hierarchically\norganize or structure your controllers into sub-directories. CodeIgniter\npermits you to do this.\n\nSimply create sub-directories under the main *application/controllers/*\none and place your controller classes within them.\n\n.. note:: When using this feature the first segment of your URI must\n\tspecify the folder. For example, let's say you have a controller located\n\there::\n\n\t\tapplication/controllers/products/Shoes.php\n\n\tTo call the above controller your URI will look something like this::\n\n\t\texample.com/index.php/products/shoes/show/123\n\nEach of your sub-directories may contain a default controller which will be\ncalled if the URL contains *only* the sub-directory. Simply put a controller\nin there that matches the name of your 'default_controller' as specified in\nyour *application/config/routes.php* file.\n\nCodeIgniter also permits you to remap your URIs using its :doc:`URI\nRouting <routing>` feature.\n\nClass Constructors\n==================\n\nIf you intend to use a constructor in any of your Controllers, you\n**MUST** place the following line of code in it::\n\n\tparent::__construct();\n\nThe reason this line is necessary is because your local constructor will\nbe overriding the one in the parent controller class so we need to\nmanually call it.\n\nExample::\n\n\t<?php\n\tclass Blog extends CI_Controller {\n\n\t\tpublic function __construct()\n\t\t{\n\t\t\tparent::__construct();\n\t\t\t// Your own constructor code\n\t\t}\n\t}\n\nConstructors are useful if you need to set some default values, or run a\ndefault process when your class is instantiated. Constructors can't\nreturn a value, but they can do some default work.\n\nReserved method names\n=====================\n\nSince your controller classes will extend the main application\ncontroller you must be careful not to name your methods identically to\nthe ones used by that class, otherwise your local functions will\noverride them. See :doc:`Reserved Names <reserved_names>` for a full\nlist.\n\n.. important:: You should also never have a method named identically\n\tto its class name. If you do, and there is no ``__construct()``\n\tmethod in the same class, then your e.g. ``Index::index()``\n\tmethod will be executed as a class constructor! This is a PHP4\n\tbackwards-compatibility feature.\n\nThat's it!\n==========\n\nThat, in a nutshell, is all there is to know about controllers.\n"
  },
  {
    "path": "user_guide_src/source/general/core_classes.rst",
    "content": "############################\nCreating Core System Classes\n############################\n\nEvery time CodeIgniter runs there are several base classes that are\ninitialized automatically as part of the core framework. It is possible,\nhowever, to swap any of the core system classes with your own versions\nor even extend the core versions.\n\n**Most users will never have any need to do this, but the option to\nreplace or extend them does exist for those who would like to\nsignificantly alter the CodeIgniter core.**\n\n.. note:: Messing with a core system class has a lot of implications, so\n\tmake sure you know what you are doing before attempting it.\n\nSystem Class List\n=================\n\nThe following is a list of the core system files that are invoked every\ntime CodeIgniter runs:\n\n-  Benchmark\n-  Config\n-  Controller\n-  Exceptions\n-  Hooks\n-  Input\n-  Language\n-  Loader\n-  Log\n-  Output\n-  Router\n-  Security\n-  URI\n-  Utf8\n\nReplacing Core Classes\n======================\n\nTo use one of your own system classes instead of a default one simply\nplace your version inside your local *application/core/* directory::\n\n\tapplication/core/some_class.php\n\nIf this directory does not exist you can create it.\n\nAny file named identically to one from the list above will be used\ninstead of the one normally used.\n\nPlease note that your class must use CI as a prefix. For example, if\nyour file is named Input.php the class will be named::\n\n\tclass CI_Input {\n\n\t}\n\nExtending Core Class\n====================\n\nIf all you need to do is add some functionality to an existing library -\nperhaps add a method or two - then it's overkill to replace the entire\nlibrary with your version. In this case it's better to simply extend the\nclass. Extending a class is nearly identical to replacing a class with a\ncouple exceptions:\n\n-  The class declaration must extend the parent class.\n-  Your new class name and filename must be prefixed with MY\\_ (this\n   item is configurable. See below.).\n\nFor example, to extend the native Input class you'll create a file named\napplication/core/MY_Input.php, and declare your class with::\n\n\tclass MY_Input extends CI_Input {\n\n\t}\n\n.. note:: If you need to use a constructor in your class make sure you\n\textend the parent constructor::\n\n\t\tclass MY_Input extends CI_Input {\n\n\t\t\tpublic function __construct()\n\t\t\t{\n\t\t\t\tparent::__construct();\n\t\t\t\t// Your own constructor code\n\t\t\t}\n\t\t}\n\n**Tip:** Any functions in your class that are named identically to the\nmethods in the parent class will be used instead of the native ones\n(this is known as \"method overriding\"). This allows you to substantially\nalter the CodeIgniter core.\n\nIf you are extending the Controller core class, then be sure to extend\nyour new class in your application controller's constructors.\n\n::\n\n\tclass Welcome extends MY_Controller {\n\n\t\tpublic function index()\n\t\t{\n\t\t\t$this->load->view('welcome_message');\n\t\t}\n\t}\n\nSetting Your Own Prefix\n-----------------------\n\nTo set your own sub-class prefix, open your\n*application/config/config.php* file and look for this item::\n\n\t$config['subclass_prefix'] = 'MY_';\n\nPlease note that all native CodeIgniter libraries are prefixed\nwith CI\\_ so DO NOT use that as your prefix.\n"
  },
  {
    "path": "user_guide_src/source/general/creating_drivers.rst",
    "content": "################\nCreating Drivers\n################\n\nDriver Directory and File Structure\n===================================\n\nSample driver directory and file structure layout:\n\n-  /application/libraries/Driver_name\n\n   -  Driver_name.php\n   -  drivers\n\n      -  Driver_name_subclass_1.php\n      -  Driver_name_subclass_2.php\n      -  Driver_name_subclass_3.php\n\n.. note:: In order to maintain compatibility on case-sensitive\n\tfile systems, the Driver_name directory must be\n\tnamed in the format returned by ``ucfirst()``.\n\n.. note:: The Driver library's architecture is such that\n\tthe subclasses don't extend and therefore don't inherit\n\tproperties or methods of the main driver."
  },
  {
    "path": "user_guide_src/source/general/creating_libraries.rst",
    "content": "##################\nCreating Libraries\n##################\n\nWhen we use the term \"Libraries\" we are normally referring to the\nclasses that are located in the libraries directory and described in the\nClass Reference of this user guide. In this case, however, we will\ninstead describe how you can create your own libraries within your\napplication/libraries directory in order to maintain separation between\nyour local resources and the global framework resources.\n\nAs an added bonus, CodeIgniter permits your libraries to extend native\nclasses if you simply need to add some functionality to an existing\nlibrary. Or you can even replace native libraries just by placing\nidentically named versions in your *application/libraries* directory.\n\nIn summary:\n\n-  You can create entirely new libraries.\n-  You can extend native libraries.\n-  You can replace native libraries.\n\nThe page below explains these three concepts in detail.\n\n.. note:: The Database classes can not be extended or replaced with your\n\town classes. All other classes are able to be replaced/extended.\n\nStorage\n=======\n\nYour library classes should be placed within your *application/libraries*\ndirectory, as this is where CodeIgniter will look for them when they are\ninitialized.\n\nNaming Conventions\n==================\n\n-  File names must be capitalized. For example: Myclass.php\n-  Class declarations must be capitalized. For example: class Myclass\n-  Class names and file names must match.\n\nThe Class File\n==============\n\nClasses should have this basic prototype::\n\n\t<?php\n\tdefined('BASEPATH') OR exit('No direct script access allowed'); \n\n\tclass Someclass {\n\n\t\tpublic function some_method()\n\t\t{\n\t\t}\n\t}\n\n.. note:: We are using the name Someclass purely as an example.\n\nUsing Your Class\n================\n\nFrom within any of your :doc:`Controller <controllers>` methods you\ncan initialize your class using the standard::\n\n\t$this->load->library('someclass');\n\nWhere *someclass* is the file name, without the \".php\" file extension.\nYou can submit the file name capitalized or lower case. CodeIgniter\ndoesn't care.\n\nOnce loaded you can access your class using the lower case version::\n\n\t$this->someclass->some_method();  // Object instances will always be lower case\n\nPassing Parameters When Initializing Your Class\n===============================================\n\nIn the library loading method you can dynamically pass data as an\narray via the second parameter and it will be passed to your class\nconstructor::\n\n\t$params = array('type' => 'large', 'color' => 'red');\n\n\t$this->load->library('someclass', $params);\n\nIf you use this feature you must set up your class constructor to expect\ndata::\n\n\t<?php defined('BASEPATH') OR exit('No direct script access allowed');\n\n\tclass Someclass {\n\n\t\tpublic function __construct($params)\n\t\t{\n\t\t\t// Do something with $params\n\t\t}\n\t}\n\nYou can also pass parameters stored in a config file. Simply create a\nconfig file named identically to the class file name and store it in\nyour *application/config/* directory. Note that if you dynamically pass\nparameters as described above, the config file option will not be\navailable.\n\nUtilizing CodeIgniter Resources within Your Library\n===================================================\n\nTo access CodeIgniter's native resources within your library use the\n``get_instance()`` method. This method returns the CodeIgniter super\nobject.\n\nNormally from within your controller methods you will call any of the\navailable CodeIgniter methods using the ``$this`` construct::\n\n\t$this->load->helper('url');\n\t$this->load->library('session');\n\t$this->config->item('base_url');\n\t// etc.\n\n``$this``, however, only works directly within your controllers, your\nmodels, or your views. If you would like to use CodeIgniter's classes\nfrom within your own custom classes you can do so as follows:\n\nFirst, assign the CodeIgniter object to a variable::\n\n\t$CI =& get_instance();\n\nOnce you've assigned the object to a variable, you'll use that variable\n*instead* of ``$this``::\n\n\t$CI =& get_instance();\n\n\t$CI->load->helper('url');\n\t$CI->load->library('session');\n\t$CI->config->item('base_url');\n\t// etc.\n\n.. note:: You'll notice that the above ``get_instance()`` function is being\n\tpassed by reference::\n\t\n\t\t$CI =& get_instance();\n\n\tThis is very important. Assigning by reference allows you to use the\n\toriginal CodeIgniter object rather than creating a copy of it.\n\nHowever, since a library is a class, it would be better if you\ntake full advantage of the OOP principles. So, in order to\nbe able to use the CodeIgniter super-object in all of the class\nmethods, you're encouraged to assign it to a property instead::\n\n\tclass Example_library {\n\n\t\tprotected $CI;\n\n\t\t// We'll use a constructor, as you can't directly call a function\n\t\t// from a property definition.\n\t\tpublic function __construct()\n\t\t{\n\t\t\t// Assign the CodeIgniter super-object\n\t\t\t$this->CI =& get_instance();\n\t\t}\n\n\t\tpublic function foo()\n\t\t{\n\t\t\t$this->CI->load->helper('url');\n\t\t\tredirect();\n\t\t}\n\n\t\tpublic function bar()\n\t\t{\n\t\t\techo $this->CI->config->item('base_url');\n\t\t}\n\n\t}\n\nReplacing Native Libraries with Your Versions\n=============================================\n\nSimply by naming your class files identically to a native library will\ncause CodeIgniter to use it instead of the native one. To use this\nfeature you must name the file and the class declaration exactly the\nsame as the native library. For example, to replace the native Email\nlibrary you'll create a file named *application/libraries/Email.php*,\nand declare your class with::\n\n\tclass CI_Email {\n\t\n\t}\n\nNote that most native classes are prefixed with CI\\_.\n\nTo load your library you'll see the standard loading method::\n\n\t$this->load->library('email');\n\n.. note:: At this time the Database classes can not be replaced with\n\tyour own versions.\n\nExtending Native Libraries\n==========================\n\nIf all you need to do is add some functionality to an existing library -\nperhaps add a method or two - then it's overkill to replace the entire\nlibrary with your version. In this case it's better to simply extend the\nclass. Extending a class is nearly identical to replacing a class with a\ncouple exceptions:\n\n-  The class declaration must extend the parent class.\n-  Your new class name and filename must be prefixed with MY\\_ (this\n   item is configurable. See below.).\n\nFor example, to extend the native Email class you'll create a file named\n*application/libraries/MY_Email.php*, and declare your class with::\n\n\tclass MY_Email extends CI_Email {\n\n\t}\n\nIf you need to use a constructor in your class make sure you\nextend the parent constructor::\n\n\tclass MY_Email extends CI_Email {\n\n\t\tpublic function __construct($config = array())\n\t\t{\n\t\t\tparent::__construct($config);\n\t\t\t// Your own constructor code\n\t\t}\n\n\t}\n\n.. note:: Not all of the libraries have the same (or any) parameters\n\tin their constructor. Take a look at the library that you're\n\textending first to see how it should be implemented.\n\nLoading Your Sub-class\n----------------------\n\nTo load your sub-class you'll use the standard syntax normally used. DO\nNOT include your prefix. For example, to load the example above, which\nextends the Email class, you will use::\n\n\t$this->load->library('email');\n\nOnce loaded you will use the class variable as you normally would for\nthe class you are extending. In the case of the email class all calls\nwill use::\n\n\t$this->email->some_method();\n\nSetting Your Own Prefix\n-----------------------\n\nTo set your own sub-class prefix, open your\n*application/config/config.php* file and look for this item::\n\n\t$config['subclass_prefix'] = 'MY_';\n\nPlease note that all native CodeIgniter libraries are prefixed with CI\\_\nso DO NOT use that as your prefix.\n"
  },
  {
    "path": "user_guide_src/source/general/credits.rst",
    "content": "#######\nCredits\n#######\n\nCodeIgniter was originally developed by `Rick Ellis <https://ellislab.com/>`_\n(CEO of `EllisLab, Inc. <https://ellislab.com/>`_). The framework was written for\nperformance in the real world, with many of the class libraries, helpers, and\nsub-systems borrowed from the code-base of `ExpressionEngine\n<https://ellislab.com/expressionengine>`_.\n\nIt was, for years, developed and maintained by EllisLab, the ExpressionEngine\nDevelopment Team and a group of community members called the Reactor Team.\n\nIn 2014, CodeIgniter was acquired by the `British Columbia Institute of Technology\n<https://www.bcit.ca/>`_ and was then officially announced as a community-maintained\nproject.\n\nBleeding edge development is spearheaded by the handpicked contributors\nof the Reactor Team.\n\nA hat tip goes to Ruby on Rails for inspiring us to create a PHP framework, and\nfor bringing frameworks into the general consciousness of the web community."
  },
  {
    "path": "user_guide_src/source/general/drivers.rst",
    "content": "#########################\nUsing CodeIgniter Drivers\n#########################\n\nDrivers are a special type of Library that has a parent class and any\nnumber of potential child classes. Child classes have access to the\nparent class, but not their siblings. Drivers provide an elegant syntax\nin your :doc:`controllers <controllers>` for libraries that benefit\nfrom or require being broken down into discrete classes.\n\nDrivers are found in the *system/libraries/* directory, in their own\nsub-directory which is identically named to the parent library class.\nAlso inside that directory is a subdirectory named drivers, which\ncontains all of the possible child class files.\n\nTo use a driver you will initialize it within a controller using the\nfollowing initialization method::\n\n\t$this->load->driver('class_name');\n\nWhere class name is the name of the driver class you want to invoke. For\nexample, to load a driver named \"Some_parent\" you would do this::\n\n\t$this->load->driver('some_parent');\n\nMethods of that class can then be invoked with::\n\n\t$this->some_parent->some_method();\n\nThe child classes, the drivers themselves, can then be called directly\nthrough the parent class, without initializing them::\n\n\t$this->some_parent->child_one->some_method();\n\t$this->some_parent->child_two->another_method();\n\nCreating Your Own Drivers\n=========================\n\nPlease read the section of the user guide that discusses how to :doc:`create\nyour own drivers <creating_drivers>`."
  },
  {
    "path": "user_guide_src/source/general/environments.rst",
    "content": "##############################\nHandling Multiple Environments\n##############################\n\nDevelopers often desire different system behavior depending on whether\nan application is running in a development or production environment.\nFor example, verbose error output is something that would be useful\nwhile developing an application, but it may also pose a security issue\nwhen \"live\".\n\nThe ENVIRONMENT Constant\n========================\n\nBy default, CodeIgniter comes with the environment constant set to use\nthe value provided in ``$_SERVER['CI_ENV']``, otherwise defaults to\n'development'. At the top of index.php, you will see::\n\n\tdefine('ENVIRONMENT', isset($_SERVER['CI_ENV']) ? $_SERVER['CI_ENV'] : 'development');\n\nThis server variable can be set in your .htaccess file, or Apache \nconfig using `SetEnv <https://httpd.apache.org/docs/2.2/mod/mod_env.html#setenv>`_. \nAlternative methods are available for nginx and other servers, or you can \nremove this logic entirely and set the constant based on the server's IP address.\n\nIn addition to affecting some basic framework behavior (see the next\nsection), you may use this constant in your own development to\ndifferentiate between which environment you are running in.\n\nEffects On Default Framework Behavior\n=====================================\n\nThere are some places in the CodeIgniter system where the ENVIRONMENT\nconstant is used. This section describes how default framework behavior\nis affected.\n\nError Reporting\n---------------\n\nSetting the ENVIRONMENT constant to a value of 'development' will cause\nall PHP errors to be rendered to the browser when they occur.\nConversely, setting the constant to 'production' will disable all error\noutput. Disabling error reporting in production is a :doc:`good security\npractice <security>`.\n\nConfiguration Files\n-------------------\n\nOptionally, you can have CodeIgniter load environment-specific\nconfiguration files. This may be useful for managing things like\ndiffering API keys across multiple environments. This is described in\nmore detail in the environment section of the :doc:`Config Class\n<../libraries/config>` documentation."
  },
  {
    "path": "user_guide_src/source/general/errors.rst",
    "content": "##############\nError Handling\n##############\n\nCodeIgniter lets you build error reporting into your applications using\nthe functions described below. In addition, it has an error logging\nclass that permits error and debugging messages to be saved as text\nfiles.\n\n.. note:: By default, CodeIgniter displays all PHP errors. You might\n\twish to change this behavior once your development is complete. You'll\n\tfind the error_reporting() function located at the top of your main\n\tindex.php file. Disabling error reporting will NOT prevent log files\n\tfrom being written if there are errors.\n\nUnlike most systems in CodeIgniter, the error functions are simple\nprocedural interfaces that are available globally throughout the\napplication. This approach permits error messages to get triggered\nwithout having to worry about class/function scoping.\n\nCodeIgniter also returns a status code whenever a portion of the core\ncalls ``exit()``. This exit status code is separate from the HTTP status\ncode, and serves as a notice to other processes that may be watching of\nwhether the script completed successfully, or if not, what kind of\nproblem it encountered that caused it to abort. These values are\ndefined in *application/config/constants.php*. While exit status codes\nare most useful in CLI settings, returning the proper code helps server\nsoftware keep track of your scripts and the health of your application.\n\nThe following functions let you generate errors:\n\n.. php:function:: show_error($message, $status_code, $heading = 'An Error Was Encountered')\n\n\t:param\tmixed\t$message: Error message\n\t:param\tint\t$status_code: HTTP Response status code\n\t:param\tstring\t$heading: Error page heading\n\t:rtype:\tvoid\n\n\tThis function will display the error message supplied to it using\n\tthe error template appropriate to your execution::\n\n\t\tapplication/views/errors/html/error_general.php\n\n\tor:\n\n\t\tapplication/views/errors/cli/error_general.php\n\n\tThe optional parameter ``$status_code`` determines what HTTP status\n\tcode should be sent with the error. If ``$status_code`` is less\n\tthan 100, the HTTP status code will be set to 500, and the exit\n\tstatus code will be set to ``$status_code + EXIT__AUTO_MIN``.\n\tIf that value is larger than ``EXIT__AUTO_MAX``, or if\n\t``$status_code`` is 100 or higher, the exit status code will be set\n\tto ``EXIT_ERROR``.\n\tYou can check in *application/config/constants.php* for more detail.\n\n.. php:function:: show_404($page = '', $log_error = TRUE)\n\n\t:param\tstring\t$page: URI string\n\t:param\tbool\t$log_error: Whether to log the error\n\t:rtype:\tvoid\n\n\tThis function will display the 404 error message supplied to it\n\tusing the error template appropriate to your execution::\n\n\t\tapplication/views/errors/html/error_404.php\n\n\tor:\n\n\t\tapplication/views/errors/cli/error_404.php\n\n\tThe function expects the string passed to it to be the file path to\n\tthe page that isn't found. The exit status code will be set to\n\t``EXIT_UNKNOWN_FILE``.\n\tNote that CodeIgniter automatically shows 404 messages if\n\tcontrollers are not found.\n\n\tCodeIgniter automatically logs any ``show_404()`` calls. Setting the\n\toptional second parameter to FALSE will skip logging.\n\n.. php:function:: log_message($level, $message)\n\n\t:param\tstring\t$level: Log level: 'error', 'debug' or 'info'\n\t:param\tstring\t$message: Message to log\n\t:rtype:\tvoid\n\n\tThis function lets you write messages to your log files. You must\n\tsupply one of three \"levels\" in the first parameter, indicating what\n\ttype of message it is (debug, error, info), with the message itself\n\tin the second parameter.\n\n\tExample::\n\n\t\tif ($some_var == '')\n\t\t{\n\t\t\tlog_message('error', 'Some variable did not contain a value.');\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlog_message('debug', 'Some variable was correctly set');\n\t\t}\n\n\t\tlog_message('info', 'The purpose of some variable is to provide some value.');\n\n\tThere are three message types:\n\n\t#. Error Messages. These are actual errors, such as PHP errors or\n\t   user errors.\n\t#. Debug Messages. These are messages that assist in debugging. For\n\t   example, if a class has been initialized, you could log this as\n\t   debugging info.\n\t#. Informational Messages. These are the lowest priority messages,\n\t   simply giving information regarding some process.\n\n\t.. note:: In order for the log file to actually be written, the\n\t\t*logs/* directory must be writable. In addition, you must\n\t\tset the \"threshold\" for logging in\n\t\t*application/config/config.php*. You might, for example,\n\t\tonly want error messages to be logged, and not the other\n\t\ttwo types. If you set it to zero logging will be disabled.\n"
  },
  {
    "path": "user_guide_src/source/general/helpers.rst",
    "content": "################\nHelper Functions\n################\n\nHelpers, as the name suggests, help you with tasks. Each helper file is\nsimply a collection of functions in a particular category. There are **URL\nHelpers**, that assist in creating links, there are Form Helpers that help\nyou create form elements, **Text Helpers** perform various text formatting\nroutines, **Cookie Helpers** set and read cookies, File Helpers help you\ndeal with files, etc.\n\nUnlike most other systems in CodeIgniter, Helpers are not written in an\nObject Oriented format. They are simple, procedural functions. Each\nhelper function performs one specific task, with no dependence on other\nfunctions.\n\nCodeIgniter does not load Helper Files by default, so the first step in\nusing a Helper is to load it. Once loaded, it becomes globally available\nin your :doc:`controller <../general/controllers>` and\n:doc:`views <../general/views>`.\n\nHelpers are typically stored in your **system/helpers**, or\n**application/helpers directory**. CodeIgniter will look first in your\n**application/helpers directory**. If the directory does not exist or the\nspecified helper is not located there CI will instead look in your\nglobal *system/helpers/* directory.\n\nLoading a Helper\n================\n\nLoading a helper file is quite simple using the following method::\n\n\t$this->load->helper('name');\n\nWhere **name** is the file name of the helper, without the .php file\nextension or the \"helper\" part.\n\nFor example, to load the **URL Helper** file, which is named\n**url_helper.php**, you would do this::\n\n\t$this->load->helper('url');\n\nA helper can be loaded anywhere within your controller methods (or\neven within your View files, although that's not a good practice), as\nlong as you load it before you use it. You can load your helpers in your\ncontroller constructor so that they become available automatically in\nany function, or you can load a helper in a specific function that needs\nit.\n\n.. note:: The Helper loading method above does not return a value, so\n\tdon't try to assign it to a variable. Just use it as shown.\n\nLoading Multiple Helpers\n========================\n\nIf you need to load more than one helper you can specify them in an\narray, like this::\n\n\t$this->load->helper(\n\t\tarray('helper1', 'helper2', 'helper3')\n\t);\n\nAuto-loading Helpers\n====================\n\nIf you find that you need a particular helper globally throughout your\napplication, you can tell CodeIgniter to auto-load it during system\ninitialization. This is done by opening the **application/config/autoload.php**\nfile and adding the helper to the autoload array.\n\nUsing a Helper\n==============\n\nOnce you've loaded the Helper File containing the function you intend to\nuse, you'll call it the way you would a standard PHP function.\n\nFor example, to create a link using the ``anchor()`` function in one of\nyour view files you would do this::\n\n\t<?php echo anchor('blog/comments', 'Click Here');?>\n\nWhere \"Click Here\" is the name of the link, and \"blog/comments\" is the\nURI to the controller/method you wish to link to.\n\n\"Extending\" Helpers\n===================\n\nTo \"extend\" Helpers, create a file in your **application/helpers/** folder\nwith an identical name to the existing Helper, but prefixed with **MY\\_**\n(this item is configurable. See below.).\n\nIf all you need to do is add some functionality to an existing helper -\nperhaps add a function or two, or change how a particular helper\nfunction operates - then it's overkill to replace the entire helper with\nyour version. In this case it's better to simply \"extend\" the Helper.\n\n.. note:: The term \"extend\" is used loosely since Helper functions are\n\tprocedural and discrete and cannot be extended in the traditional\n\tprogrammatic sense. Under the hood, this gives you the ability to\n\tadd to or or to replace the functions a Helper provides.\n\nFor example, to extend the native **Array Helper** you'll create a file\nnamed **application/helpers/MY_array_helper.php**, and add or override\nfunctions::\n\n\t// any_in_array() is not in the Array Helper, so it defines a new function\n\tfunction any_in_array($needle, $haystack)\n\t{\n\t\t$needle = is_array($needle) ? $needle : array($needle);\n\n\t\tforeach ($needle as $item)\n\t\t{\n\t\t\tif (in_array($item, $haystack))\n\t\t\t{\n\t\t\t\treturn TRUE;\n\t\t\t}\n\t        }\n\n\t\treturn FALSE;\n\t}\n\n\t// random_element() is included in Array Helper, so it overrides the native function\n\tfunction random_element($array)\n\t{\n\t\tshuffle($array);\n\t\treturn array_pop($array);\n\t}\n\nSetting Your Own Prefix\n-----------------------\n\nThe filename prefix for \"extending\" Helpers is the same used to extend\nlibraries and core classes. To set your own prefix, open your\n**application/config/config.php** file and look for this item::\n\n\t$config['subclass_prefix'] = 'MY_';\n\nPlease note that all native CodeIgniter libraries are prefixed with **CI\\_**\nso DO NOT use that as your prefix.\n\nNow What?\n=========\n\nIn the Table of Contents you'll find a list of all the available Helper\nFiles. Browse each one to see what they do."
  },
  {
    "path": "user_guide_src/source/general/hooks.rst",
    "content": "####################################\nHooks - Extending the Framework Core\n####################################\n\nCodeIgniter's Hooks feature provides a means to tap into and modify the\ninner workings of the framework without hacking the core files. When\nCodeIgniter runs it follows a specific execution process, diagramed in\nthe :doc:`Application Flow <../overview/appflow>` page. There may be\ninstances, however, where you'd like to cause some action to take place\nat a particular stage in the execution process. For example, you might\nwant to run a script right before your controllers get loaded, or right\nafter, or you might want to trigger one of your own scripts in some\nother location.\n\nEnabling Hooks\n==============\n\nThe hooks feature can be globally enabled/disabled by setting the\nfollowing item in the **application/config/config.php** file::\n\n\t$config['enable_hooks'] = TRUE;\n\nDefining a Hook\n===============\n\nHooks are defined in the **application/config/hooks.php** file.\nEach hook is specified as an array with this prototype::\n\n\t$hook['pre_controller'] = array(\n\t\t'class'    => 'MyClass',\n\t\t'function' => 'Myfunction',\n\t\t'filename' => 'Myclass.php',\n\t\t'filepath' => 'hooks',\n\t\t'params'   => array('beer', 'wine', 'snacks')\n\t);\n\n**Notes:**\n\nThe array index correlates to the name of the particular hook point you\nwant to use. In the above example the hook point is pre_controller. A\nlist of hook points is found below. The following items should be\ndefined in your associative hook array:\n\n-  **class** The name of the class you wish to invoke. If you prefer to\n   use a procedural function instead of a class, leave this item blank.\n-  **function** The function (or method) name you wish to call.\n-  **filename** The file name containing your class/function.\n-  **filepath** The name of the directory containing your script.\n   Note:\n   Your script must be located in a directory INSIDE your *application/*\n   directory, so the file path is relative to that directory. For example,\n   if your script is located in *application/hooks/*, you will simply use\n   'hooks' as your filepath. If your script is located in\n   *application/hooks/utilities/* you will use 'hooks/utilities' as your\n   filepath. No trailing slash.\n-  **params** Any parameters you wish to pass to your script. This item\n   is optional.\n\nYou can also use lambda/anoymous functions (or closures) as hooks, with\na simpler syntax::\n\n\t$hook['post_controller'] = function()\n\t{\n\t\t/* do something here */\n\t};\n\nMultiple Calls to the Same Hook\n===============================\n\nIf want to use the same hook point with more than one script, simply\nmake your array declaration multi-dimensional, like this::\n\n\t$hook['pre_controller'][] = array(\n\t\t'class'    => 'MyClass',\n\t\t'function' => 'MyMethod',\n\t\t'filename' => 'Myclass.php',\n\t\t'filepath' => 'hooks',\n\t\t'params'   => array('beer', 'wine', 'snacks')\n\t);\n\n\t$hook['pre_controller'][] = array(\n\t\t'class'    => 'MyOtherClass',\n\t\t'function' => 'MyOtherMethod',\n\t\t'filename' => 'Myotherclass.php',\n\t\t'filepath' => 'hooks',\n\t\t'params'   => array('red', 'yellow', 'blue')\n\t);\n\nNotice the brackets after each array index::\n\n\t$hook['pre_controller'][]\n\nThis permits you to have the same hook point with multiple scripts. The\norder you define your array will be the execution order.\n\nHook Points\n===========\n\nThe following is a list of available hook points.\n\n-  **pre_system**\n   Called very early during system execution. Only the benchmark and\n   hooks class have been loaded at this point. No routing or other\n   processes have happened.\n-  **pre_controller**\n   Called immediately prior to any of your controllers being called.\n   All base classes, routing, and security checks have been done.\n-  **post_controller_constructor**\n   Called immediately after your controller is instantiated, but prior\n   to any method calls happening.\n-  **post_controller**\n   Called immediately after your controller is fully executed.\n-  **display_override**\n   Overrides the ``_display()`` method, used to send the finalized page\n   to the web browser at the end of system execution. This permits you\n   to use your own display methodology. Note that you will need to\n   reference the CI superobject with ``$this->CI =& get_instance()`` and\n   then the finalized data will be available by calling\n   ``$this->CI->output->get_output()``.\n-  **cache_override**\n   Enables you to call your own method instead of the ``_display_cache()``\n   method in the :doc:`Output Library <../libraries/output>`. This permits\n   you to use your own cache display mechanism.\n-  **post_system**\n   Called after the final rendered page is sent to the browser, at the\n   end of system execution after the finalized data is sent to the\n   browser."
  },
  {
    "path": "user_guide_src/source/general/index.rst",
    "content": "##############\nGeneral Topics\n##############\n\n.. toctree::\n\t:titlesonly:\n\t\n\turls\n\tcontrollers\n\treserved_names\n\tviews\n\tmodels\n\tHelpers <helpers>\n\tlibraries\n\tcreating_libraries\n\tdrivers\n\tcreating_drivers\n\tcore_classes\n\tancillary_classes\n\thooks\n\tautoloader\n\tcommon_functions\n\tcompatibility_functions\n\trouting\n\terrors\n\tCaching <caching>\n\tprofiling\n\tcli\n\tmanaging_apps\n\tenvironments\n\talternative_php\n\tsecurity\n\tPHP Style Guide <styleguide>"
  },
  {
    "path": "user_guide_src/source/general/libraries.rst",
    "content": "###########################\nUsing CodeIgniter Libraries\n###########################\n\nAll of the available libraries are located in your *system/libraries/*\ndirectory. In most cases, to use one of these classes involves initializing\nit within a :doc:`controller <controllers>` using the following\ninitialization method::\n\n\t$this->load->library('class_name');\n\nWhere 'class_name' is the name of the class you want to invoke. For\nexample, to load the :doc:`Form Validation Library\n<../libraries/form_validation>` you would do this::\n\n\t$this->load->library('form_validation');\n\nOnce initialized you can use it as indicated in the user guide page\ncorresponding to that class.\n\nAdditionally, multiple libraries can be loaded at the same time by\npassing an array of libraries to the load method.\n\nExample::\n\n\t$this->load->library(array('email', 'table'));\n\nCreating Your Own Libraries\n===========================\n\nPlease read the section of the user guide that discusses how to\n:doc:`create your own libraries <creating_libraries>`."
  },
  {
    "path": "user_guide_src/source/general/managing_apps.rst",
    "content": "##########################\nManaging your Applications\n##########################\n\nBy default it is assumed that you only intend to use CodeIgniter to\nmanage one application, which you will build in your *application/*\ndirectory. It is possible, however, to have multiple sets of\napplications that share a single CodeIgniter installation, or even to\nrename or relocate your application directory.\n\nRenaming the Application Directory\n==================================\n\nIf you would like to rename your application directory you may do so\nas long as you open your main index.php file and set its name using\nthe ``$application_folder`` variable::\n\n\t$application_folder = 'application';\n\nRelocating your Application Directory\n=====================================\n\nIt is possible to move your application directory to a different\nlocation on your server than your web root. To do so open\nyour main index.php and set a *full server path* in the\n``$application_folder`` variable::\n\n\t$application_folder = '/path/to/your/application';\n\nRunning Multiple Applications with one CodeIgniter Installation\n===============================================================\n\nIf you would like to share a common CodeIgniter installation to manage\nseveral different applications simply put all of the directories located\ninside your application directory into their own sub-directory.\n\nFor example, let's say you want to create two applications, named \"foo\"\nand \"bar\". You could structure your application directories like this::\n\n\tapplications/foo/\n\tapplications/foo/config/\n\tapplications/foo/controllers/\n\tapplications/foo/libraries/\n\tapplications/foo/models/\n\tapplications/foo/views/\n\tapplications/bar/\n\tapplications/bar/config/\n\tapplications/bar/controllers/\n\tapplications/bar/libraries/\n\tapplications/bar/models/\n\tapplications/bar/views/\n\nTo select a particular application for use requires that you open your\nmain index.php file and set the ``$application_folder`` variable. For\nexample, to select the \"foo\" application for use you would do this::\n\n\t$application_folder = 'applications/foo';\n\n.. note:: Each of your applications will need its own index.php file\n\twhich calls the desired application. The index.php file can be named\n\tanything you want."
  },
  {
    "path": "user_guide_src/source/general/models.rst",
    "content": "######\nModels\n######\n\nModels are **optionally** available for those who want to use a more\ntraditional MVC approach.\n\n.. contents:: Page Contents\n\nWhat is a Model?\n================\n\nModels are PHP classes that are designed to work with information in\nyour database. For example, let's say you use CodeIgniter to manage a\nblog. You might have a model class that contains functions to insert,\nupdate, and retrieve your blog data. Here is an example of what such a\nmodel class might look like::\n\n\tclass Blog_model extends CI_Model {\n\n\t\tpublic $title;\n\t\tpublic $content;\n\t\tpublic $date;\n\n\t\tpublic function get_last_ten_entries()\n\t\t{\n\t\t\t$query = $this->db->get('entries', 10);\n\t\t\treturn $query->result();\n\t\t}\n\n\t\tpublic function insert_entry()\n\t\t{\n\t\t\t$this->title\t= $_POST['title']; // please read the below note\n\t\t\t$this->content\t= $_POST['content'];\n\t\t\t$this->date\t= time();\n\n\t\t\t$this->db->insert('entries', $this);\n\t\t}\n\n\t\tpublic function update_entry()\n\t\t{\n\t\t\t$this->title\t= $_POST['title'];\n\t\t\t$this->content\t= $_POST['content'];\n\t\t\t$this->date\t= time();\n\n\t\t\t$this->db->update('entries', $this, array('id' => $_POST['id']));\n\t\t}\n\n\t}\n\n.. note:: The methods in the above example use the :doc:`Query Builder\n\t<../database/query_builder>` database methods.\n\n.. note:: For the sake of simplicity in this example we're using ``$_POST``\n\tdirectly. This is generally bad practice, and a more common approach\n\twould be to use the :doc:`Input Library <../libraries/input>`\n\t``$this->input->post('title')``.\n\nAnatomy of a Model\n==================\n\nModel classes are stored in your **application/models/** directory.\nThey can be nested within sub-directories if you want this type of\norganization.\n\nThe basic prototype for a model class is this::\n\n\tclass Model_name extends CI_Model {\n\n\t}\n\nWhere **Model_name** is the name of your class. Class names **must** have\nthe first letter capitalized with the rest of the name lowercase. Make\nsure your class extends the base Model class.\n\nThe file name must match the class name. For example, if this is your class::\n\n\tclass User_model extends CI_Model {\n\n\t}\n\nYour file will be this::\n\n\tapplication/models/User_model.php\n\nLoading a Model\n===============\n\nYour models will typically be loaded and called from within your\n:doc:`controller <controllers>` methods. To load a model you will use\nthe following method::\n\n\t$this->load->model('model_name');\n\nIf your model is located in a sub-directory, include the relative path\nfrom your models directory. For example, if you have a model located at\n*application/models/blog/Queries.php* you'll load it using::\n\n\t$this->load->model('blog/queries');\n\nOnce loaded, you will access your model methods using an object with the\nsame name as your class::\n\n\t$this->load->model('model_name');\n\n\t$this->model_name->method();\n\nIf you would like your model assigned to a different object name you can\nspecify it via the second parameter of the loading method::\n\n\t$this->load->model('model_name', 'foobar');\n\n\t$this->foobar->method();\n\nHere is an example of a controller, that loads a model, then serves a\nview::\n\n\tclass Blog_controller extends CI_Controller {\n\n\t\tpublic function blog()\n\t\t{\n\t\t\t$this->load->model('blog');\n\n\t\t\t$data['query'] = $this->blog->get_last_ten_entries();\n\n\t\t\t$this->load->view('blog', $data);\n\t\t}\n\t}\n\t\n\nAuto-loading Models\n===================\n\nIf you find that you need a particular model globally throughout your\napplication, you can tell CodeIgniter to auto-load it during system\ninitialization. This is done by opening the\n**application/config/autoload.php** file and adding the model to the\nautoload array.\n\nConnecting to your Database\n===========================\n\nWhen a model is loaded it does **NOT** connect automatically to your\ndatabase. The following options for connecting are available to you:\n\n-  You can connect using the standard database methods :doc:`described\n   here <../database/connecting>`, either from within your\n   Controller class or your Model class.\n-  You can tell the model loading method to auto-connect by passing\n   TRUE (boolean) via the third parameter, and connectivity settings,\n   as defined in your database config file will be used::\n\n\t$this->load->model('model_name', '', TRUE);\n\n-  You can manually pass database connectivity settings via the third\n   parameter::\n\n\t$config['hostname'] = 'localhost';\n\t$config['username'] = 'myusername';\n\t$config['password'] = 'mypassword';\n\t$config['database'] = 'mydatabase';\n\t$config['dbdriver'] = 'mysqli';\n\t$config['dbprefix'] = '';\n\t$config['pconnect'] = FALSE;\n\t$config['db_debug'] = TRUE;\n\n\t$this->load->model('model_name', '', $config);\n"
  },
  {
    "path": "user_guide_src/source/general/profiling.rst",
    "content": "##########################\nProfiling Your Application\n##########################\n\nThe Profiler Class will display benchmark results, queries you have run,\nand ``$_POST`` data at the bottom of your pages. This information can be\nuseful during development in order to help with debugging and\noptimization.\n\nInitializing the Class\n======================\n\n.. important:: This class does NOT need to be initialized. It is loaded\n\tautomatically by the :doc:`Output Library <../libraries/output>`\n\tif profiling is enabled as shown below.\n\nEnabling the Profiler\n=====================\n\nTo enable the profiler place the following line anywhere within your\n:doc:`Controller <controllers>` methods::\n\n\t$this->output->enable_profiler(TRUE);\n\nWhen enabled a report will be generated and inserted at the bottom of\nyour pages.\n\nTo disable the profiler you will use::\n\n\t$this->output->enable_profiler(FALSE);\n\nSetting Benchmark Points\n========================\n\nIn order for the Profiler to compile and display your benchmark data you\nmust name your mark points using specific syntax.\n\nPlease read the information on setting Benchmark points in the\n:doc:`Benchmark Library <../libraries/benchmark>` page.\n\nEnabling and Disabling Profiler Sections\n========================================\n\nEach section of Profiler data can be enabled or disabled by setting a\ncorresponding config variable to TRUE or FALSE. This can be done one of\ntwo ways. First, you can set application wide defaults with the\n*application/config/profiler.php* config file.\n\nExample::\n\n\t$config['config']          = FALSE;\n\t$config['queries']         = FALSE;\n\nIn your controllers, you can override the defaults and config file\nvalues by calling the ``set_profiler_sections()`` method of the\n:doc:`Output Library <../libraries/output>`::\n\n\t$sections = array(\n\t\t'config'  => TRUE,\n\t\t'queries' => TRUE\n\t);\n\n\t$this->output->set_profiler_sections($sections);\n\nAvailable sections and the array key used to access them are described\nin the table below.\n\n======================= =================================================================== ========\nKey                     Description                                                         Default\n======================= =================================================================== ========\n**benchmarks**          Elapsed time of Benchmark points and total execution time           TRUE\n**config**              CodeIgniter Config variables                                        TRUE\n**controller_info**     The Controller class and method requested                           TRUE\n**get**                 Any GET data passed in the request                                  TRUE\n**http_headers**        The HTTP headers for the current request                            TRUE\n**memory_usage**        Amount of memory consumed by the current request, in bytes          TRUE\n**post**                Any POST data passed in the request                                 TRUE\n**queries**             Listing of all database queries executed, including execution time  TRUE\n**uri_string**          The URI of the current request                                      TRUE\n**session_data**        Data stored in the current session                                  TRUE\n**query_toggle_count**  The number of queries after which the query block will default to   25\n                        hidden.\n======================= =================================================================== ========\n\n.. note:: Disabling the :doc:`save_queries </database/configuration>` setting in\n\tyour database configuration will also effectively disable profiling for\n\tdatabase queries and render the 'queries' setting above useless. You can\n\toptionally override this setting with ``$this->db->save_queries = TRUE;``.\n\tWithout this setting you won't be able to view the queries or the\n\t`last_query <database/helpers>`."
  },
  {
    "path": "user_guide_src/source/general/requirements.rst",
    "content": "###################\nServer Requirements\n###################\n\n`PHP <https://secure.php.net/>`_ version 5.6 or newer is recommended.\n\nIt should work on 5.4.8 as well, but we strongly advise you NOT to run\nsuch old versions of PHP, because of potential security and performance\nissues, as well as missing features.\n\nA database is required for most web application programming.\nCurrently supported databases are:\n\n  - MySQL (5.1+) via the *mysql* (deprecated), *mysqli* and *pdo* drivers\n  - Oracle via the *oci8* and *pdo* drivers\n  - PostgreSQL via the *postgre* and *pdo* drivers\n  - MS SQL via the *mssql*, *sqlsrv* (version 2005 and above only) and *pdo* drivers\n  - SQLite via the *sqlite3* and *pdo* drivers\n  - CUBRID via the *cubrid* and *pdo* drivers\n  - Interbase/Firebird via the *ibase* and *pdo* drivers\n  - ODBC via the *odbc* and *pdo* drivers (you should know that ODBC is actually an abstraction layer)\n"
  },
  {
    "path": "user_guide_src/source/general/reserved_names.rst",
    "content": "##############\nReserved Names\n##############\n\nIn order to help out, CodeIgniter uses a series of function, method,\nclass and variable names in its operation. Because of this, some names\ncannot be used by a developer. Following is a list of reserved names\nthat cannot be used.\n\nController names\n----------------\n\nSince your controller classes will extend the main application\ncontroller you must be careful not to name your methods identically to\nthe ones used by that class, otherwise your local methods will\noverride them. The following is a list of reserved names. Do not name\nyour controller any of these:\n\n-  CI_Controller\n-  Default\n-  index\n\nFunctions\n---------\n\n-  :php:func:`is_php()`\n-  :php:func:`is_really_writable()`\n-  ``load_class()``\n-  ``is_loaded()``\n-  ``get_config()``\n-  :php:func:`config_item()`\n-  :php:func:`show_error()`\n-  :php:func:`show_404()`\n-  :php:func:`log_message()`\n-  :php:func:`set_status_header()`\n-  :php:func:`get_mimes()`\n-  :php:func:`html_escape()`\n-  :php:func:`remove_invisible_characters()`\n-  :php:func:`is_https()`\n-  :php:func:`function_usable()`\n-  :php:func:`get_instance()`\n-  ``_error_handler()``\n-  ``_exception_handler()``\n-  ``_stringify_attributes()``\n\nVariables\n---------\n\n-  ``$config``\n-  ``$db``\n-  ``$lang``\n\nConstants\n---------\n\n-  ENVIRONMENT\n-  FCPATH\n-  SELF\n-  BASEPATH\n-  APPPATH\n-  VIEWPATH\n-  CI_VERSION\n-  MB_ENABLED\n-  ICONV_ENABLED\n-  UTF8_ENABLED\n-  FILE_READ_MODE\n-  FILE_WRITE_MODE\n-  DIR_READ_MODE\n-  DIR_WRITE_MODE\n-  FOPEN_READ\n-  FOPEN_READ_WRITE\n-  FOPEN_WRITE_CREATE_DESTRUCTIVE\n-  FOPEN_READ_WRITE_CREATE_DESTRUCTIVE\n-  FOPEN_WRITE_CREATE\n-  FOPEN_READ_WRITE_CREATE\n-  FOPEN_WRITE_CREATE_STRICT\n-  FOPEN_READ_WRITE_CREATE_STRICT\n-  SHOW_DEBUG_BACKTRACE\n-  EXIT_SUCCESS\n-  EXIT_ERROR\n-  EXIT_CONFIG\n-  EXIT_UNKNOWN_FILE\n-  EXIT_UNKNOWN_CLASS\n-  EXIT_UNKNOWN_METHOD\n-  EXIT_USER_INPUT\n-  EXIT_DATABASE\n-  EXIT__AUTO_MIN\n-  EXIT__AUTO_MAX"
  },
  {
    "path": "user_guide_src/source/general/routing.rst",
    "content": "###########\nURI Routing\n###########\n\nTypically there is a one-to-one relationship between a URL string and\nits corresponding controller class/method. The segments in a URI\nnormally follow this pattern::\n\n\texample.com/class/function/id/\n\nIn some instances, however, you may want to remap this relationship so\nthat a different class/method can be called instead of the one\ncorresponding to the URL.\n\nFor example, let's say you want your URLs to have this prototype::\n\n\texample.com/product/1/\n\texample.com/product/2/\n\texample.com/product/3/\n\texample.com/product/4/\n\nNormally the second segment of the URL is reserved for the method\nname, but in the example above it instead has a product ID. To\novercome this, CodeIgniter allows you to remap the URI handler.\n\nSetting your own routing rules\n==============================\n\nRouting rules are defined in your *application/config/routes.php* file.\nIn it you'll see an array called ``$route`` that permits you to specify\nyour own routing criteria. Routes can either be specified using wildcards\nor Regular Expressions.\n\nWildcards\n=========\n\nA typical wildcard route might look something like this::\n\n\t$route['product/:num'] = 'catalog/product_lookup';\n\nIn a route, the array key contains the URI to be matched, while the\narray value contains the destination it should be re-routed to. In the\nabove example, if the literal word \"product\" is found in the first\nsegment of the URL, and a number is found in the second segment, the\n\"catalog\" class and the \"product_lookup\" method are instead used.\n\nYou can match literal values or you can use two wildcard types:\n\n**(:num)** will match a segment containing only numbers.\n**(:any)** will match a segment containing any character (except for '/', which is the segment delimiter).\n\n.. note:: Wildcards are actually aliases for regular expressions, with\n\t**:any** being translated to **[^/]+** and **:num** to **[0-9]+**,\n\trespectively.\n\n.. note:: Routes will run in the order they are defined. Higher routes\n\twill always take precedence over lower ones.\n\n.. note:: Route rules are not filters! Setting a rule of e.g.\n\t'foo/bar/(:num)' will not prevent controller *Foo* and method\n\t*bar* to be called with a non-numeric value if that is a valid\n\troute.\n\nExamples\n========\n\nHere are a few routing examples::\n\n\t$route['journals'] = 'blogs';\n\nA URL containing the word \"journals\" in the first segment will be\nremapped to the \"blogs\" class.\n\n::\n\n\t$route['blog/joe'] = 'blogs/users/34';\n\nA URL containing the segments blog/joe will be remapped to the \"blogs\"\nclass and the \"users\" method. The ID will be set to \"34\".\n\n::\n\n\t$route['product/(:any)'] = 'catalog/product_lookup';\n\nA URL with \"product\" as the first segment, and anything in the second\nwill be remapped to the \"catalog\" class and the \"product_lookup\"\nmethod.\n\n::\n\n\t$route['product/(:num)'] = 'catalog/product_lookup_by_id/$1';\n\nA URL with \"product\" as the first segment, and a number in the second\nwill be remapped to the \"catalog\" class and the\n\"product_lookup_by_id\" method passing in the match as a variable to\nthe method.\n\n.. important:: Do not use leading/trailing slashes.\n\nRegular Expressions\n===================\n\nIf you prefer you can use regular expressions to define your routing\nrules. Any valid regular expression is allowed, as are back-references.\n\n.. note:: If you use back-references you must use the dollar syntax\n\trather than the double backslash syntax.\n\nA typical RegEx route might look something like this::\n\n\t$route['products/([a-z]+)/(\\d+)'] = '$1/id_$2';\n\nIn the above example, a URI similar to products/shirts/123 would instead\ncall the \"shirts\" controller class and the \"id_123\" method.\n\nWith regular expressions, you can also catch multiple segments at once.\nFor example, if a user accesses a password protected area of your web\napplication and you wish to be able to redirect them back to the same\npage after they log in, you may find this example useful::\n\n\t$route['login/(.+)'] = 'auth/login/$1';\n\n.. note:: In the above example, if the ``$1`` placeholder contains a\n\tslash, it will still be split into multiple parameters when\n\tpassed to ``Auth::login()``.\n\nFor those of you who don't know regular expressions and want to learn\nmore about them, `regular-expressions.info <https://www.regular-expressions.info/>`_\nmight be a good starting point.\n\n.. note:: You can also mix and match wildcards with regular expressions.\n\nCallbacks\n=========\n\nYou can also use callbacks in place of the normal routing rules to process\nthe back-references. Example::\n\n\t$route['products/([a-zA-Z]+)/edit/(\\d+)'] = function ($product_type, $id)\n\t{\n\t\treturn 'catalog/product_edit/' . strtolower($product_type) . '/' . $id;\n\t};\n\nUsing HTTP verbs in routes\n==========================\n\nIt is possible to use HTTP verbs (request method) to define your routing rules.\nThis is particularly useful when building RESTful applications. You can use standard HTTP\nverbs (GET, PUT, POST, DELETE, PATCH) or a custom one such (e.g. PURGE). HTTP verb rules\nare case-insensitive. All you need to do is to add the verb as an array key to your route.\nExample::\n\n\t$route['products']['put'] = 'product/insert';\n\nIn the above example, a PUT request to URI \"products\" would call the ``Product::insert()``\ncontroller method.\n\n::\n\n\t$route['products/(:num)']['DELETE'] = 'product/delete/$1';\n\nA DELETE request to URL with \"products\" as first the segment and a number in the second will be\nmapped to the ``Product::delete()`` method, passing the numeric value as the first parameter.\n\nUsing HTTP verbs is of course, optional.\n\nReserved Routes\n===============\n\nThere are three reserved routes::\n\n\t$route['default_controller'] = 'welcome';\n\nThis route points to the action that should be executed if the URI contains\nno data, which will be the case when people load your root URL.\nThe setting accepts a **controller/method** value and ``index()`` would be\nthe default method if you don't specify one. In the above example, it is\n``Welcome::index()`` that would be called.\n\n.. note:: You can NOT use a directory as a part of this setting!\n\nYou are encouraged to always have a default route as otherwise a 404 page\nwill appear by default.\n\n::\n\n\t$route['404_override'] = '';\n\nThis route indicates which controller class should be loaded if the\nrequested controller is not found. It will override the default 404\nerror page. Same per-directory rules as with 'default_controller'\napply here as well.\n\nIt won't affect to the ``show_404()`` function, which will\ncontinue loading the default *error_404.php* file at\n*application/views/errors/error_404.php*.\n\n::\n\n\t$route['translate_uri_dashes'] = FALSE;\n\nAs evident by the boolean value, this is not exactly a route. This\noption enables you to automatically replace dashes ('-') with\nunderscores in the controller and method URI segments, thus saving you\nadditional route entries if you need to do that.\nThis is required, because the dash isn't a valid class or method name\ncharacter and would cause a fatal error if you try to use it.\n"
  },
  {
    "path": "user_guide_src/source/general/security.rst",
    "content": "########\nSecurity\n########\n\nThis page describes some \"best practices\" regarding web security, and\ndetails CodeIgniter's internal security features.\n\n.. note:: If you came here looking for a security contact, please refer to\n\tour `Contribution Guide <../contributing/index>`.\n\nURI Security\n============\n\nCodeIgniter is fairly restrictive regarding which characters it allows\nin your URI strings in order to help minimize the possibility that\nmalicious data can be passed to your application. URIs may only contain\nthe following:\n\n-  Alpha-numeric text (latin characters only)\n-  Tilde: ~\n-  Percent sign: %\n-  Period: .\n-  Colon: :\n-  Underscore: \\_\n-  Dash: -\n-  Space\n\nRegister_globals\n================\n\nDuring system initialization all global variables that are found to exist\nin the ``$_GET``, ``$_POST``, ``$_REQUEST`` and ``$_COOKIE`` are unset.\n\nThe unsetting routine is effectively the same as *register_globals = off*.\n\ndisplay_errors\n==============\n\nIn production environments, it is typically desirable to \"disable\" PHP's\nerror reporting by setting the internal *display_errors* flag to a value\nof 0. This disables native PHP errors from being rendered as output,\nwhich may potentially contain sensitive information.\n\nSetting CodeIgniter's **ENVIRONMENT** constant in index.php to a value of\n**\\'production\\'** will turn off these errors. In development mode, it is\nrecommended that a value of 'development' is used. More information\nabout differentiating between environments can be found on the\n:doc:`Handling Environments <environments>` page.\n\nmagic_quotes_runtime\n====================\n\nThe *magic_quotes_runtime* directive is turned off during system\ninitialization so that you don't have to remove slashes when retrieving\ndata from your database.\n\n**************\nBest Practices\n**************\n\nBefore accepting any data into your application, whether it be POST data\nfrom a form submission, COOKIE data, URI data, XML-RPC data, or even\ndata from the SERVER array, you are encouraged to practice this three\nstep approach:\n\n#. Validate the data to ensure it conforms to the correct type, length,\n   size, etc.\n#. Filter the data as if it were tainted.\n#. Escape the data before submitting it into your database or outputting\n   it to a browser.\n\nCodeIgniter provides the following functions and tips to assist you\nin this process:\n\nXSS Filtering\n=============\n\nCodeIgniter comes with a Cross Site Scripting filter. This filter\nlooks for commonly used techniques to embed malicious JavaScript into\nyour data, or other types of code that attempt to hijack cookies or\ndo other malicious things. The XSS Filter is described\n:doc:`here <../libraries/security>`.\n\n.. note:: XSS filtering should *only be performed on output*. Filtering\n\tinput data may modify the data in undesirable ways, including\n\tstripping special characters from passwords, which reduces\n\tsecurity instead of improving it.\n\nCSRF protection\n===============\n\nCSRF stands for Cross-Site Request Forgery, which is the process of an\nattacker tricking their victim into unknowingly submitting a request.\n\nCodeIgniter provides CSRF protection out of the box, which will get\nautomatically triggered for every non-GET HTTP request, but also needs\nyou to create your submit forms in a certain way. This is explained in\nthe :doc:`Security Library <../libraries/security>` documentation.\n\nPassword handling\n=================\n\nIt is *critical* that you handle passwords in your application properly.\n\nUnfortunately, many developers don't know how to do that, and the web is\nfull of outdated or otherwise wrongful advices, which doesn't help.\n\nWe would like to give you a list of combined do's and don'ts to help you\nwith that. Please read below.\n\n-  DO NOT store passwords in plain-text format.\n\n   Always **hash** your passwords.\n\n-  DO NOT use Base64 or similar encoding for storing passwords.\n\n   This is as good as storing them in plain-text. Really. Do **hashing**,\n   not *encoding*.\n\n   Encoding, and encryption too, are two-way processes. Passwords are\n   secrets that must only be known to their owner, and thus must work\n   only in one direction. Hashing does that - there's *no* un-hashing or\n   de-hashing, but there is decoding and decryption.\n\n-  DO NOT use weak or broken hashing algorithms like MD5 or SHA1.\n\n   These algorithms are old, proven to be flawed, and not designed for\n   password hashing in the first place.\n\n   Also, DON'T invent your own algorithms.\n\n   Only use strong password hashing algorithms like BCrypt, which is used\n   in PHP's own `Password Hashing <https://secure.php.net/password>`_ functions.\n\n   Please use them, even if you're not running PHP 5.5+, CodeIgniter\n   provides them for you.\n\n-  DO NOT ever display or send a password in plain-text format!\n\n   Even to the password's owner, if you need a \"Forgotten password\"\n   feature, just randomly generate a new, one-time (this is also important)\n   password and send that instead.\n\n-  DO NOT put unnecessary limits on your users' passwords.\n\n   If you're using a hashing algorithm other than BCrypt (which has a limit\n   of 72 characters), you should set a relatively high limit on password\n   lengths in order to mitigate DoS attacks - say, 1024 characters.\n\n   Other than that however, there's no point in forcing a rule that a\n   password can only be up to a number of characters, or that it can't\n   contain a certain set of special characters.\n\n   Not only does this **reduce** security instead of improving it, but\n   there's literally no reason to do it. No technical limitations and\n   no (practical) storage constraints apply once you've hashed them, none!\n\nValidate input data\n===================\n\nCodeIgniter has a :doc:`Form Validation Library\n<../libraries/form_validation>` that assists you in\nvalidating, filtering, and prepping your data.\n\nEven if that doesn't work for your use case however, be sure to always\nvalidate and sanitize all input data. For example, if you expect a numeric\nstring for an input variable, you can check for that with ``is_numeric()``\nor ``ctype_digit()``. Always try to narrow down your checks to a certain\npattern.\n\nHave it in mind that this includes not only ``$_POST`` and ``$_GET``\nvariables, but also cookies, the user-agent string and basically\n*all data that is not created directly by your own code*.\n\n\nEscape all data before database insertion\n=========================================\n\nNever insert information into your database without escaping it.\nPlease see the section that discusses :doc:`database queries\n<../database/queries>` for more information.\n\nHide your files\n===============\n\nAnother good security practice is to only leave your *index.php*\nand \"assets\" (e.g. .js, css and image files) under your server's\n*webroot* directory (most commonly named \"htdocs/\"). These are\nthe only files that you would need to be accessible from the web.\n\nAllowing your visitors to see anything else would potentially\nallow them to access sensitive data, execute scripts, etc.\n\nIf you're not allowed to do that, you can try using a .htaccess\nfile to restrict access to those resources.\n\nCodeIgniter will have an index.html file in all of its\ndirectories in an attempt to hide some of this data, but have\nit in mind that this is not enough to prevent a serious\nattacker.\n"
  },
  {
    "path": "user_guide_src/source/general/styleguide.rst",
    "content": "###############\nPHP Style Guide\n###############\n\n\nThe following page describes the coding styles adhered to when\ncontributing to the development of CodeIgniter. There is no requirement\nto use these styles in your own CodeIgniter application, though they\nare recommended.\n\n.. contents:: Table of Contents\n\nFile Format\n===========\n\nFiles should be saved with Unicode (UTF-8) encoding. The BOM should\n*not* be used. Unlike UTF-16 and UTF-32, there's no byte order to\nindicate in a UTF-8 encoded file, and the BOM can have a negative side\neffect in PHP of sending output, preventing the application from being\nable to set its own headers. Unix line endings should be used (LF).\n\nHere is how to apply these settings in some of the more common text\neditors. Instructions for your text editor may vary; check your text\neditor's documentation.\n\nTextMate\n''''''''\n\n#. Open the Application Preferences\n#. Click Advanced, and then the \"Saving\" tab\n#. In \"File Encoding\", select \"UTF-8 (recommended)\"\n#. In \"Line Endings\", select \"LF (recommended)\"\n#. *Optional:* Check \"Use for existing files as well\" if you wish to\n   modify the line endings of files you open to your new preference.\n\nBBEdit\n''''''\n\n#. Open the Application Preferences\n#. Select \"Text Encodings\" on the left.\n#. In \"Default text encoding for new documents\", select \"Unicode (UTF-8,\n   no BOM)\"\n#. *Optional:* In \"If file's encoding can't be guessed, use\", select\n   \"Unicode (UTF-8, no BOM)\"\n#. Select \"Text Files\" on the left.\n#. In \"Default line breaks\", select \"Mac OS X and Unix (LF)\"\n\nPHP Closing Tag\n===============\n\nThe PHP closing tag on a PHP document **?>** is optional to the PHP\nparser. However, if used, any whitespace following the closing tag,\nwhether introduced by the developer, user, or an FTP application, can\ncause unwanted output, PHP errors, or if the latter are suppressed,\nblank pages. For this reason, all PHP files MUST OMIT the PHP closing\ntag and end with a single empty line instead.\n\nFile Naming\n===========\n\nClass files must be named in a Ucfirst-like manner, while any other file name\n(configurations, views, generic scripts, etc.) should be in all lowercase.\n\n**INCORRECT**::\n\n\tsomelibrary.php\n\tsomeLibrary.php\n\tSOMELIBRARY.php\n\tSome_Library.php\n\n\tApplication_config.php\n\tApplication_Config.php\n\tapplicationConfig.php\n\n**CORRECT**::\n\n\tSomelibrary.php\n\tSome_library.php\n\n\tapplicationconfig.php\n\tapplication_config.php\n\nFurthermore, class file names should match the name of the class itself.\nFor example, if you have a class named `Myclass`, then its filename must\nbe **Myclass.php**.\n\nClass and Method Naming\n=======================\n\nClass names should always start with an uppercase letter. Multiple words\nshould be separated with an underscore, and not CamelCased.\n\n**INCORRECT**::\n\n\tclass superclass\n\tclass SuperClass\n\n**CORRECT**::\n\n\tclass Super_class\n\n::\n\n\tclass Super_class {\n\n\t\tpublic function __construct()\n\t\t{\n\n\t\t}\n\t}\n\nClass methods should be entirely lowercased and named to clearly\nindicate their function, preferably including a verb. Try to avoid\noverly long and verbose names. Multiple words should be separated\nwith an underscore.\n\n**INCORRECT**::\n\n\tfunction fileproperties()\t\t// not descriptive and needs underscore separator\n\tfunction fileProperties()\t\t// not descriptive and uses CamelCase\n\tfunction getfileproperties()\t\t// Better!  But still missing underscore separator\n\tfunction getFileProperties()\t\t// uses CamelCase\n\tfunction get_the_file_properties_from_the_file()\t// wordy\n\n**CORRECT**::\n\n\tfunction get_file_properties()\t// descriptive, underscore separator, and all lowercase letters\n\nVariable Names\n==============\n\nThe guidelines for variable naming are very similar to those used for\nclass methods. Variables should contain only lowercase letters,\nuse underscore separators, and be reasonably named to indicate their\npurpose and contents. Very short, non-word variables should only be used\nas iterators in for() loops.\n\n**INCORRECT**::\n\n\t$j = 'foo';\t\t// single letter variables should only be used in for() loops\n\t$Str\t\t\t// contains uppercase letters\n\t$bufferedText\t\t// uses CamelCasing, and could be shortened without losing semantic meaning\n\t$groupid\t\t// multiple words, needs underscore separator\n\t$name_of_last_city_used\t// too long\n\n**CORRECT**::\n\n\tfor ($j = 0; $j < 10; $j++)\n\t$str\n\t$buffer\n\t$group_id\n\t$last_city\n\nCommenting\n==========\n\nIn general, code should be commented prolifically. It not only helps\ndescribe the flow and intent of the code for less experienced\nprogrammers, but can prove invaluable when returning to your own code\nmonths down the line. There is not a required format for comments, but\nthe following are recommended.\n\n`DocBlock <https://manual.phpdoc.org/HTMLSmartyConverter/HandS/phpDocumentor/tutorial_phpDocumentor.howto.pkg.html#basics.docblock>`_\nstyle comments preceding class, method, and property declarations so they can be\npicked up by IDEs::\n\n\t/**\n\t * Super Class\n\t *\n\t * @package\tPackage Name\n\t * @subpackage\tSubpackage\n\t * @category\tCategory\n\t * @author\tAuthor Name\n\t * @link\thttp://example.com\n\t */\n\tclass Super_class {\n\n::\n\n\t/**\n\t * Encodes string for use in XML\n\t *\n\t * @param\tstring\t$str\tInput string\n\t * @return\tstring\n\t */\n\tfunction xml_encode($str)\n\n::\n\n\t/**\n\t * Data for class manipulation\n\t *\n\t * @var\tarray\n\t */\n\tpublic $data = array();\n\nUse single line comments within code, leaving a blank line between large\ncomment blocks and code.\n\n::\n\n\t// break up the string by newlines\n\t$parts = explode(\"\\n\", $str);\n\n\t// A longer comment that needs to give greater detail on what is\n\t// occurring and why can use multiple single-line comments.  Try to\n\t// keep the width reasonable, around 70 characters is the easiest to\n\t// read.  Don't hesitate to link to permanent external resources\n\t// that may provide greater detail:\n\t//\n\t// http://example.com/information_about_something/in_particular/\n\n\t$parts = $this->foo($parts);\n\nConstants\n=========\n\nConstants follow the same guidelines as do variables, except constants\nshould always be fully uppercase. *Always use CodeIgniter constants when\nappropriate, i.e. SLASH, LD, RD, PATH_CACHE, etc.*\n\n**INCORRECT**::\n\n\tmyConstant\t// missing underscore separator and not fully uppercase\n\tN\t\t// no single-letter constants\n\tS_C_VER\t\t// not descriptive\n\t$str = str_replace('{foo}', 'bar', $str);\t// should use LD and RD constants\n\n**CORRECT**::\n\n\tMY_CONSTANT\n\tNEWLINE\n\tSUPER_CLASS_VERSION\n\t$str = str_replace(LD.'foo'.RD, 'bar', $str);\n\nTRUE, FALSE, and NULL\n=====================\n\n**TRUE**, **FALSE**, and **NULL** keywords should always be fully\nuppercase.\n\n**INCORRECT**::\n\n\tif ($foo == true)\n\t$bar = false;\n\tfunction foo($bar = null)\n\n**CORRECT**::\n\n\tif ($foo == TRUE)\n\t$bar = FALSE;\n\tfunction foo($bar = NULL)\n\nLogical Operators\n=================\n\nUse of the ``||`` \"or\" comparison operator is discouraged, as its clarity\non some output devices is low (looking like the number 11, for instance).\n``&&`` is preferred over ``AND`` but either are acceptable, and a space should\nalways precede and follow ``!``.\n\n**INCORRECT**::\n\n\tif ($foo || $bar)\n\tif ($foo AND $bar)  // okay but not recommended for common syntax highlighting applications\n\tif (!$foo)\n\tif (! is_array($foo))\n\n**CORRECT**::\n\n\tif ($foo OR $bar)\n\tif ($foo && $bar) // recommended\n\tif ( ! $foo)\n\tif ( ! is_array($foo))\n\t\n\nComparing Return Values and Typecasting\n=======================================\n\nSome PHP functions return FALSE on failure, but may also have a valid\nreturn value of \"\" or 0, which would evaluate to FALSE in loose\ncomparisons. Be explicit by comparing the variable type when using these\nreturn values in conditionals to ensure the return value is indeed what\nyou expect, and not a value that has an equivalent loose-type\nevaluation.\n\nUse the same stringency in returning and checking your own variables.\nUse **===** and **!==** as necessary.\n\n**INCORRECT**::\n\n\t// If 'foo' is at the beginning of the string, strpos will return a 0,\n\t// resulting in this conditional evaluating as TRUE\n\tif (strpos($str, 'foo') == FALSE)\n\n**CORRECT**::\n\n\tif (strpos($str, 'foo') === FALSE)\n\n**INCORRECT**::\n\n\tfunction build_string($str = \"\")\n\t{\n\t\tif ($str == \"\")\t// uh-oh!  What if FALSE or the integer 0 is passed as an argument?\n\t\t{\n\n\t\t}\n\t}\n\n**CORRECT**::\n\n\tfunction build_string($str = \"\")\n\t{\n\t\tif ($str === \"\")\n\t\t{\n\n\t\t}\n\t}\n\n\nSee also information regarding `typecasting\n<https://secure.php.net/manual/en/language.types.type-juggling.php#language.types.typecasting>`_,\nwhich can be quite useful. Typecasting has a slightly different effect\nwhich may be desirable. When casting a variable as a string, for\ninstance, NULL and boolean FALSE variables become empty strings, 0 (and\nother numbers) become strings of digits, and boolean TRUE becomes \"1\"::\n\n\t$str = (string) $str; // cast $str as a string\n\nDebugging Code\n==============\n\nDo not leave debugging code in your submissions, even when commented out.\nThings such as ``var_dump()``, ``print_r()``, ``die()``/``exit()`` should not be included\nin your code unless it serves a specific purpose other than debugging.\n\nWhitespace in Files\n===================\n\nNo whitespace can precede the opening PHP tag or follow the closing PHP\ntag. Output is buffered, so whitespace in your files can cause output to\nbegin before CodeIgniter outputs its content, leading to errors and an\ninability for CodeIgniter to send proper headers.\n\nCompatibility\n=============\n\nCodeIgniter recommends PHP 5.6 or newer to be used, but it should be\ncompatible with PHP 5.4.8. Your code must either be compatible with this\nrequirement, provide a suitable fallback, or be an optional feature that\ndies quietly without affecting a user's application.\n\nAdditionally, do not use PHP functions that require non-default libraries\nto be installed unless your code contains an alternative method when the\nfunction is not available.\n\nOne File per Class\n==================\n\nUse separate files for each class, unless the classes are *closely related*.\nAn example of a CodeIgniter file that contains multiple classes is the \nXmlrpc library file.\n\nWhitespace\n==========\n\nUse tabs for whitespace in your code, not spaces. This may seem like a\nsmall thing, but using tabs instead of whitespace allows the developer\nlooking at your code to have indentation at levels that they prefer and\ncustomize in whatever application they use. And as a side benefit, it\nresults in (slightly) more compact files, storing one tab character\nversus, say, four space characters.\n\nLine Breaks\n===========\n\nFiles must be saved with Unix line breaks. This is more of an issue for\ndevelopers who work in Windows, but in any case ensure that your text\neditor is setup to save files with Unix line breaks.\n\nCode Indenting\n==============\n\nUse Allman style indenting. With the exception of Class declarations,\nbraces are always placed on a line by themselves, and indented at the\nsame level as the control statement that \"owns\" them.\n\n**INCORRECT**::\n\n\tfunction foo($bar) {\n\t\t// ...\n\t}\n\n\tforeach ($arr as $key => $val) {\n\t\t// ...\n\t}\n\n\tif ($foo == $bar) {\n\t\t// ...\n\t} else {\n\t\t// ...\n\t}\n\n\tfor ($i = 0; $i < 10; $i++)\n\t\t{\n\t\tfor ($j = 0; $j < 10; $j++)\n\t\t\t{\n\t\t\t// ...\n\t\t\t}\n\t\t}\n\t\t\n\ttry {\n\t\t// ...\n\t}\n\tcatch() {\n\t\t// ...\n\t}\n\n**CORRECT**::\n\n\tfunction foo($bar)\n\t{\n\t\t// ...\n\t}\n\n\tforeach ($arr as $key => $val)\n\t{\n\t\t// ...\n\t}\n\n\tif ($foo == $bar)\n\t{\n\t\t// ...\n\t}\n\telse\n\t{\n\t\t// ...\n\t}\n\n\tfor ($i = 0; $i < 10; $i++)\n\t{\n\t\tfor ($j = 0; $j < 10; $j++)\n\t\t{\n\t\t\t// ...\n\t\t}\n\t}\n\t\n\ttry \n\t{\n\t\t// ...\n\t}\n\tcatch()\n\t{\n\t\t// ...\n\t}\n\nBracket and Parenthetic Spacing\n===============================\n\nIn general, parenthesis and brackets should not use any additional\nspaces. The exception is that a space should always follow PHP control\nstructures that accept arguments with parenthesis (declare, do-while,\nelseif, for, foreach, if, switch, while), to help distinguish them from\nfunctions and increase readability.\n\n**INCORRECT**::\n\n\t$arr[ $foo ] = 'foo';\n\n**CORRECT**::\n\n\t$arr[$foo] = 'foo'; // no spaces around array keys\n\n**INCORRECT**::\n\n\tfunction foo ( $bar )\n\t{\n\n\t}\n\n**CORRECT**::\n\n\tfunction foo($bar) // no spaces around parenthesis in function declarations\n\t{\n\n\t}\n\n**INCORRECT**::\n\n\tforeach( $query->result() as $row )\n\n**CORRECT**::\n\n\tforeach ($query->result() as $row) // single space following PHP control structures, but not in interior parenthesis\n\nLocalized Text\n==============\n\nCodeIgniter libraries should take advantage of corresponding language files\nwhenever possible.\n\n**INCORRECT**::\n\n\treturn \"Invalid Selection\";\n\n**CORRECT**::\n\n\treturn $this->lang->line('invalid_selection');\n\nPrivate Methods and Variables\n=============================\n\nMethods and variables that are only accessed internally,\nsuch as utility and helper functions that your public methods use for\ncode abstraction, should be prefixed with an underscore.\n\n::\n\n\tpublic function convert_text()\n\tprivate function _convert_text()\n\nPHP Errors\n==========\n\nCode must run error free and not rely on warnings and notices to be\nhidden to meet this requirement. For instance, never access a variable\nthat you did not set yourself (such as ``$_POST`` array keys) without first\nchecking to see that it ``isset()``.\n\nMake sure that your dev environment has error reporting enabled\nfor ALL users, and that display_errors is enabled in the PHP\nenvironment. You can check this setting with::\n\n\tif (ini_get('display_errors') == 1)\n\t{\n\t\texit \"Enabled\";\n\t}\n\nOn some servers where *display_errors* is disabled, and you do not have\nthe ability to change this in the php.ini, you can often enable it with::\n\n\tini_set('display_errors', 1);\n\n.. note:: Setting the `display_errors\n\t<https://secure.php.net/manual/en/errorfunc.configuration.php#ini.display-errors>`_\n\tsetting with ``ini_set()`` at runtime is not identical to having\n\tit enabled in the PHP environment. Namely, it will not have any\n\teffect if the script has fatal errors.\n\nShort Open Tags\n===============\n\nAlways use full PHP opening tags, in case a server does not have\n*short_open_tag* enabled.\n\n**INCORRECT**::\n\n\t<? echo $foo; ?>\n\n\t<?=$foo?>\n\n**CORRECT**::\n\n\t<?php echo $foo; ?>\n\n.. note:: PHP 5.4 will always have the **<?=** tag available.\n\nOne Statement Per Line\n======================\n\nNever combine statements on one line.\n\n**INCORRECT**::\n\n\t$foo = 'this'; $bar = 'that'; $bat = str_replace($foo, $bar, $bag);\n\n**CORRECT**::\n\n\t$foo = 'this';\n\t$bar = 'that';\n\t$bat = str_replace($foo, $bar, $bag);\n\nStrings\n=======\n\nAlways use single quoted strings unless you need variables parsed, and\nin cases where you do need variables parsed, use braces to prevent\ngreedy token parsing. You may also use double-quoted strings if the\nstring contains single quotes, so you do not have to use escape\ncharacters.\n\n**INCORRECT**::\n\n\t\"My String\"\t\t\t\t\t// no variable parsing, so no use for double quotes\n\t\"My string $foo\"\t\t\t\t// needs braces\n\t'SELECT foo FROM bar WHERE baz = \\'bag\\''\t// ugly\n\n**CORRECT**::\n\n\t'My String'\n\t\"My string {$foo}\"\n\t\"SELECT foo FROM bar WHERE baz = 'bag'\"\n\nSQL Queries\n===========\n\nSQL keywords are always capitalized: SELECT, INSERT, UPDATE, WHERE,\nAS, JOIN, ON, IN, etc.\n\nBreak up long queries into multiple lines for legibility, preferably\nbreaking for each clause.\n\n**INCORRECT**::\n\n\t// keywords are lowercase and query is too long for\n\t// a single line (... indicates continuation of line)\n\t$query = $this->db->query(\"select foo, bar, baz, foofoo, foobar as raboof, foobaz from exp_pre_email_addresses\n\t...where foo != 'oof' and baz != 'zab' order by foobaz limit 5, 100\");\n\n**CORRECT**::\n\n\t$query = $this->db->query(\"SELECT foo, bar, baz, foofoo, foobar AS raboof, foobaz\n\t\t\t\t\tFROM exp_pre_email_addresses\n\t\t\t\t\tWHERE foo != 'oof'\n\t\t\t\t\tAND baz != 'zab'\n\t\t\t\t\tORDER BY foobaz\n\t\t\t\t\tLIMIT 5, 100\");\n\nDefault Function Arguments\n==========================\n\nWhenever appropriate, provide function argument defaults, which helps\nprevent PHP errors with mistaken calls and provides common fallback\nvalues which can save a few lines of code. Example::\n\n\tfunction foo($bar = '', $baz = FALSE)\n"
  },
  {
    "path": "user_guide_src/source/general/urls.rst",
    "content": "################\nCodeIgniter URLs\n################\n\nBy default, URLs in CodeIgniter are designed to be search-engine and\nhuman friendly. Rather than using the standard \"query string\" approach\nto URLs that is synonymous with dynamic systems, CodeIgniter uses a\n**segment-based** approach::\n\n\texample.com/news/article/my_article\n\n.. note:: Query string URLs can be optionally enabled, as described\n\tbelow.\n\nURI Segments\n============\n\nThe segments in the URL, in following with the Model-View-Controller\napproach, usually represent::\n\n\texample.com/class/function/ID\n\n#. The first segment represents the controller **class** that should be\n   invoked.\n#. The second segment represents the class **function**, or method, that\n   should be called.\n#. The third, and any additional segments, represent the ID and any\n   variables that will be passed to the controller.\n\nThe :doc:`URI Library <../libraries/uri>` and the :doc:`URL Helper\n<../helpers/url_helper>` contain functions that make it easy to work\nwith your URI data. In addition, your URLs can be remapped using the\n:doc:`URI Routing <routing>` feature for more flexibility.\n\nRemoving the index.php file\n===========================\n\nBy default, the **index.php** file will be included in your URLs::\n\n\texample.com/index.php/news/article/my_article\n\nIf your Apache server has *mod_rewrite* enabled, you can easily remove this\nfile by using a .htaccess file with some simple rules. Here is an example\nof such a file, using the \"negative\" method in which everything is redirected\nexcept the specified items:\n\t\n.. code-block:: apache\n\n\tRewriteEngine On\n\tRewriteCond %{REQUEST_FILENAME} !-f\n\tRewriteCond %{REQUEST_FILENAME} !-d\n\tRewriteRule ^(.*)$ index.php/$1 [L]\n\nIn the above example, any HTTP request other than those for existing\ndirectories and existing files is treated as a request for your index.php file.\n\n.. note:: These specific rules might not work for all server configurations.\n\n.. note:: Make sure to also exclude from the above rule any assets that you\n\tmight need to be accessible from the outside world.\n\nAdding a URL Suffix\n===================\n\nIn your **config/config.php** file you can specify a suffix that will be\nadded to all URLs generated by CodeIgniter. For example, if a URL is\nthis::\n\n\texample.com/index.php/products/view/shoes\n\nYou can optionally add a suffix, like **.html,** making the page appear to\nbe of a certain type::\n\n\texample.com/index.php/products/view/shoes.html\n\nEnabling Query Strings\n======================\n\nIn some cases you might prefer to use query strings URLs::\n\n\tindex.php?c=products&m=view&id=345\n\nCodeIgniter optionally supports this capability, which can be enabled in\nyour **application/config.php** file. If you open your config file you'll\nsee these items::\n\n\t$config['enable_query_strings'] = FALSE;\n\t$config['controller_trigger'] = 'c';\n\t$config['function_trigger'] = 'm';\n\nIf you change \"enable_query_strings\" to TRUE this feature will become\nactive. Your controllers and functions will then be accessible using the\n\"trigger\" words you've set to invoke your controllers and methods::\n\n\tindex.php?c=controller&m=method\n\n.. note:: If you are using query strings you will have to build your own\n\tURLs, rather than utilizing the URL helpers (and other helpers\n\tthat generate URLs, like some of the form helpers) as these are\n\tdesigned to work with segment based URLs."
  },
  {
    "path": "user_guide_src/source/general/views.rst",
    "content": "#####\nViews\n#####\n\nA view is simply a web page, or a page fragment, like a header, footer,\nsidebar, etc. In fact, views can flexibly be embedded within other views\n(within other views, etc., etc.) if you need this type of hierarchy.\n\nViews are never called directly, they must be loaded by a\n:doc:`controller <controllers>`. Remember that in an MVC framework, the\nController acts as the traffic cop, so it is responsible for fetching a\nparticular view. If you have not read the\n:doc:`Controllers <controllers>` page you should do so before\ncontinuing.\n\nUsing the example controller you created in the\n:doc:`controller <controllers>` page, let's add a view to it.\n\nCreating a View\n===============\n\nUsing your text editor, create a file called blogview.php, and put this\nin it::\n\n\t<html lang=\"en\">\n\t<head>\n\t\t<title>My Blog</title>\n\t</head>\n\t<body>\n\t\t<h1>Welcome to my Blog!</h1>\n\t</body>\n\t</html>\n\t\nThen save the file in your *application/views/* directory.\n\nLoading a View\n==============\n\nTo load a particular view file you will use the following method::\n\n\t$this->load->view('name');\n\nWhere name is the name of your view file.\n\n.. note:: The .php file extension does not need to be specified\n\tunless you use something other than .php.\n\nNow, open the controller file you made earlier called Blog.php, and\nreplace the echo statement with the view loading method::\n\n\t<?php\n\tclass Blog extends CI_Controller {\n\n\t\tpublic function index()\n\t\t{\n\t\t\t$this->load->view('blogview');\n\t\t}\n\t}\n\nIf you visit your site using the URL you did earlier you should see your\nnew view. The URL was similar to this::\n\n\texample.com/index.php/blog/\n\nLoading multiple views\n======================\n\nCodeIgniter will intelligently handle multiple calls to\n``$this->load->view()`` from within a controller. If more than one call\nhappens they will be appended together. For example, you may wish to\nhave a header view, a menu view, a content view, and a footer view. That\nmight look something like this::\n\n\t<?php\n\n\tclass Page extends CI_Controller {\n\n\t\tpublic function index()\n\t\t{\n\t\t\t$data['page_title'] = 'Your title';\n\t\t\t$this->load->view('header');\n\t\t\t$this->load->view('menu');\n\t\t\t$this->load->view('content', $data);\n\t\t\t$this->load->view('footer');\n\t\t}\n\n\t}\n\nIn the example above, we are using \"dynamically added data\", which you\nwill see below.\n\nStoring Views within Sub-directories\n====================================\n\nYour view files can also be stored within sub-directories if you prefer\nthat type of organization. When doing so you will need to include the\ndirectory name loading the view. Example::\n\n\t$this->load->view('directory_name/file_name');\n\nAdding Dynamic Data to the View\n===============================\n\nData is passed from the controller to the view by way of an **array** or\nan **object** in the second parameter of the view loading method. Here\nis an example using an array::\n\n\t$data = array(\n\t\t'title' => 'My Title',\n\t\t'heading' => 'My Heading',\n\t\t'message' => 'My Message'\n\t);\n\n\t$this->load->view('blogview', $data);\n\nAnd here's an example using an object::\n\n\t$data = new Someclass();\n\t$this->load->view('blogview', $data);\n\n.. note:: If you use an object, the class variables will be turned\n\tinto array elements.\n\nLet's try it with your controller file. Open it add this code::\n\n\t<?php\n\tclass Blog extends CI_Controller {\n\n\t\tpublic function index()\n\t\t{\n\t\t\t$data['title'] = \"My Real Title\";\n\t\t\t$data['heading'] = \"My Real Heading\";\n\n\t\t\t$this->load->view('blogview', $data);\n\t\t}\n\t}\n\nNow open your view file and change the text to variables that correspond\nto the array keys in your data::\n\n\t<html lang=\"en\">\n\t<head>\n\t\t<title><?php echo $title;?></title>\n\t</head>\n\t<body>\n\t\t<h1><?php echo $heading;?></h1>\n\t</body>\n\t</html>\n\nThen load the page at the URL you've been using and you should see the\nvariables replaced.\n\nCreating Loops\n==============\n\nThe data array you pass to your view files is not limited to simple\nvariables. You can pass multi dimensional arrays, which can be looped to\ngenerate multiple rows. For example, if you pull data from your database\nit will typically be in the form of a multi-dimensional array.\n\nHere's a simple example. Add this to your controller::\n\n\t<?php\n\tclass Blog extends CI_Controller {\n\n\t\tpublic function index()\n\t\t{\n\t\t\t$data['todo_list'] = array('Clean House', 'Call Mom', 'Run Errands');\n\n\t\t\t$data['title'] = \"My Real Title\";\n\t\t\t$data['heading'] = \"My Real Heading\";\n\n\t\t\t$this->load->view('blogview', $data);\n\t\t}\n\t}\n\nNow open your view file and create a loop::\n\n\t<html lang=\"en\">\n\t<head>\n\t\t<title><?php echo $title;?></title>\n\t</head>\n\t<body>\n\t\t<h1><?php echo $heading;?></h1>\n\t\n\t\t<h3>My Todo List</h3>\n\n\t\t<ul>\n\t\t<?php foreach ($todo_list as $item):?>\n\t\n\t\t\t<li><?php echo $item;?></li>\n\t\n\t\t<?php endforeach;?>\n\t\t</ul>\n\n\t</body>\n\t</html>\n\n.. note:: You'll notice that in the example above we are using PHP's\n\talternative syntax. If you are not familiar with it you can read about\n\tit :doc:`here <alternative_php>`.\n\nReturning views as data\n=======================\n\nThere is a third **optional** parameter lets you change the behavior of\nthe method so that it returns data as a string rather than sending it\nto your browser. This can be useful if you want to process the data in\nsome way. If you set the parameter to TRUE (boolean) it will return\ndata. The default behavior is false, which sends it to your browser.\nRemember to assign it to a variable if you want the data returned::\n\n\t$string = $this->load->view('myfile', '', TRUE);\n"
  },
  {
    "path": "user_guide_src/source/general/welcome.rst",
    "content": "######################\nWelcome to CodeIgniter\n######################\n\nCodeIgniter is an Application Development Framework - a toolkit - for\npeople who build web sites using PHP. Its goal is to enable you to\ndevelop projects much faster than you could if you were writing code\nfrom scratch, by providing a rich set of libraries for commonly needed\ntasks, as well as a simple interface and logical structure to access\nthese libraries. CodeIgniter lets you creatively focus on your project\nby minimizing the amount of code needed for a given task.\n\n***********************\nWho is CodeIgniter For?\n***********************\n\nCodeIgniter is right for you if:\n\n-  You want a framework with a small footprint.\n-  You need exceptional performance.\n-  You need broad compatibility with standard hosting accounts that run\n   a variety of PHP versions and configurations.\n-  You want a framework that requires nearly zero configuration.\n-  You want a framework that does not require you to use the command\n   line.\n-  You want a framework that does not require you to adhere to\n   restrictive coding rules.\n-  You are not interested in large-scale monolithic libraries like PEAR.\n-  You do not want to be forced to learn a templating language (although\n   a template parser is optionally available if you desire one).\n-  You eschew complexity, favoring simple solutions.\n-  You need clear, thorough documentation."
  },
  {
    "path": "user_guide_src/source/helpers/array_helper.rst",
    "content": "############\nArray Helper\n############\n\nThe Array Helper file contains functions that assist in working with\narrays.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\nLoading this Helper\n===================\n\nThis helper is loaded using the following code::\n\n\t$this->load->helper('array');\n\n\nAvailable Functions\n===================\n\nThe following functions are available:\n\n\n.. php:function:: element($item, $array[, $default = NULL])\n\n\t:param\tstring\t$item: Item to fetch from the array\n\t:param\tarray\t$array: Input array\n\t:param\tbool\t$default: What to return if the array isn't valid\n\t:returns:\tNULL on failure or the array item.\n\t:rtype:\tmixed\n\n\tLets you fetch an item from an array. The function tests whether the\n\tarray index is set and whether it has a value. If a value exists it is\n\treturned. If a value does not exist it returns NULL, or whatever you've\n\tspecified as the default value via the third parameter.\n\n\tExample::\n\n\t\t$array = array(\n\t\t\t'color'\t=> 'red',\n\t\t\t'shape'\t=> 'round',\n\t\t\t'size'\t=> ''\n\t\t);\n\n\t\techo element('color', $array); // returns \"red\"\n\t\techo element('size', $array, 'foobar'); // returns \"foobar\"\n\n\n.. php:function:: elements($items, $array[, $default = NULL])\n\n\t:param\tstring\t$item: Item to fetch from the array\n\t:param\tarray\t$array: Input array\n\t:param\tbool\t$default: What to return if the array isn't valid\n\t:returns:\tNULL on failure or the array item.\n\t:rtype:\tmixed\n\n\tLets you fetch a number of items from an array. The function tests\n\twhether each of the array indices is set. If an index does not exist it\n\tis set to NULL, or whatever you've specified as the default value via\n\tthe third parameter.\n\n\tExample::\n\n\t\t$array = array(\n\t\t\t'color' => 'red',\n\t\t\t'shape' => 'round',\n\t\t\t'radius' => '10',\n\t\t\t'diameter' => '20'\n\t\t);\n\n\t\t$my_shape = elements(array('color', 'shape', 'height'), $array);\n\n\tThe above will return the following array::\n\n\t\tarray(\n\t\t\t'color' => 'red',\n\t\t\t'shape' => 'round',\n\t\t\t'height' => NULL\n\t\t);\n\n\tYou can set the third parameter to any default value you like.\n\t::\n\n\t\t $my_shape = elements(array('color', 'shape', 'height'), $array, 'foobar');\n\n\tThe above will return the following array::\n\n\t\tarray(     \n\t\t\t'color' \t=> 'red',\n\t\t\t'shape' \t=> 'round',\n\t\t\t'height'\t=> 'foobar'\n\t\t);\n\n\tThis is useful when sending the ``$_POST`` array to one of your Models.\n\tThis prevents users from sending additional POST data to be entered into\n\tyour tables.\n\n\t::\n\n\t\t$this->load->model('post_model');\n\t\t$this->post_model->update(\n\t\t\telements(array('id', 'title', 'content'), $_POST)\n\t\t);\n\n\tThis ensures that only the id, title and content fields are sent to be\n\tupdated.\n\n\n.. php:function:: random_element($array)\n\n\t:param\tarray\t$array: Input array\n\t:returns:\tA random element from the array\n\t:rtype:\tmixed\n\n\tTakes an array as input and returns a random element from it.\n\n\tUsage example::\n\n\t\t$quotes = array(\n\t\t\t\"I find that the harder I work, the more luck I seem to have. - Thomas Jefferson\",\n\t\t\t\"Don't stay in bed, unless you can make money in bed. - George Burns\",\n\t\t\t\"We didn't lose the game; we just ran out of time. - Vince Lombardi\",\n\t\t\t\"If everything seems under control, you're not going fast enough. - Mario Andretti\",\n\t\t\t\"Reality is merely an illusion, albeit a very persistent one. - Albert Einstein\",\n\t\t\t\"Chance favors the prepared mind - Louis Pasteur\"\n\t\t);\n\n\t\techo random_element($quotes);"
  },
  {
    "path": "user_guide_src/source/helpers/captcha_helper.rst",
    "content": "##############\nCAPTCHA Helper\n##############\n\nThe CAPTCHA Helper file contains functions that assist in creating\nCAPTCHA images.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\nLoading this Helper\n===================\n\nThis helper is loaded using the following code::\n\n\t$this->load->helper('captcha');\n\nUsing the CAPTCHA helper\n========================\n\nOnce loaded you can generate a CAPTCHA like this::\n\n\t$vals = array(\n\t\t'word'\t\t=> 'Random word',\n\t\t'img_path'\t=> './captcha/',\n\t\t'img_url'\t=> 'http://example.com/captcha/',\n\t\t'font_path'\t=> './path/to/fonts/texb.ttf',\n\t\t'img_width'\t=> '150',\n\t\t'img_height'\t=> 30,\n\t\t'expiration'\t=> 7200,\n\t\t'word_length'\t=> 8,\n\t\t'font_size'\t=> 16,\n\t\t'img_id'\t=> 'Imageid',\n\t\t'pool'\t\t=> '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',\n\n\t\t// White background and border, black text and red grid\n\t\t'colors'\t=> array(\n\t\t\t'background' => array(255, 255, 255),\n\t\t\t'border' => array(255, 255, 255),\n\t\t\t'text' => array(0, 0, 0),\n\t\t\t'grid' => array(255, 40, 40)\n\t\t)\n\t);\n\n\t$cap = create_captcha($vals);\n\techo $cap['image'];\n\n-  The captcha function requires the GD image library.\n-  The **img_path** and **img_url** are both required if you want to write images to disk.\n   To create ``data:image/png;base64`` images, simply omit these options.\n-  If a **word** is not supplied, the function will generate a random\n   ASCII string. You might put together your own word library that you\n   can draw randomly from.\n-  If you do not specify a path to a TRUE TYPE font, the native ugly GD\n   font will be used.\n-  The \"captcha\" directory must be writable\n-  The **expiration** (in seconds) signifies how long an image will remain\n   in the captcha folder before it will be deleted. The default is two\n   hours.\n-  **word_length** defaults to 8, **pool** defaults to '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'\n-  **font_size** defaults to 16, the native GD font has a size limit. Specify a \"true type\" font for bigger sizes.\n-  The **img_id** will be set as the \"id\" of the captcha image.\n-  If any of the **colors** values is missing, it will be replaced by the default.\n\nAdding a Database\n-----------------\n\nIn order for the captcha function to prevent someone from submitting,\nyou will need to add the information returned from ``create_captcha()``\nto your database. Then, when the data from the form is submitted by\nthe user you will need to verify that the data exists in the database\nand has not expired.\n\nHere is a table prototype::\n\n\tCREATE TABLE captcha (  \n\t\tcaptcha_id bigint(13) unsigned NOT NULL auto_increment,  \n\t\tcaptcha_time int(10) unsigned NOT NULL,  \n\t\tip_address varchar(45) NOT NULL,  \n\t\tword varchar(20) NOT NULL,  \n\t\tPRIMARY KEY `captcha_id` (`captcha_id`),  \n\t\tKEY `word` (`word`)\n\t);\n\nHere is an example of usage with a database. On the page where the\nCAPTCHA will be shown you'll have something like this::\n\n\t$this->load->helper('captcha');\n\n\t$cap = create_captcha($vals);\n\t$data = array(     \n\t\t'captcha_time'\t=> $cap['time'],     \n\t\t'ip_address'\t=> $this->input->ip_address(),     \n\t\t'word'\t\t=> $cap['word']     \n\t);\n\n\t$query = $this->db->insert_string('captcha', $data);\n\t$this->db->query($query);\n\n\techo 'Submit the word you see below:';\n\techo $cap['image'];\n\techo '<input type=\"text\" name=\"captcha\" value=\"\" />';\n\nThen, on the page that accepts the submission you'll have something like\nthis::\n\n\t// First, delete old captchas\n\t$expiration = time() - 7200; // Two hour limit\n\t$this->db->where('captcha_time < ', $expiration)\n\t\t->delete('captcha');\n\n\t// Then see if a captcha exists:\n\t$sql = 'SELECT COUNT(*) AS count FROM captcha WHERE word = ? AND ip_address = ? AND captcha_time > ?';\n\t$binds = array($_POST['captcha'], $this->input->ip_address(), $expiration);\n\t$query = $this->db->query($sql, $binds);\n\t$row = $query->row();\n\n\tif ($row->count == 0)\n\t{     \n\t\techo 'You must submit the word that appears in the image.';\n\t}\n\nAvailable Functions\n===================\n\nThe following functions are available:\n\n.. php:function:: create_captcha($data)\n\n\t:param\tarray\t$data: Array of data for the CAPTCHA\n\t:returns:\tarray('word' => $word, 'time' => $now, 'image' => $img)\n\t:rtype:\tarray\n\n\tTakes an array of information to generate the CAPTCHA as input and\n\tcreates the image to your specifications, returning an array of\n\tassociative data about the image.\n\n\t::\n\n\t\tarray(\n\t\t\t'image'\t=> IMAGE TAG\n\t\t\t'time'\t=> TIMESTAMP (in microtime)\n\t\t\t'word'\t=> CAPTCHA WORD\n\t\t)\n\n\tThe **image** is the actual image tag::\n\n\t\t<img src=\"data:image/png;base64,RHVtbXkgZXhhbXBsZQ==\" width=\"140\" height=\"50\" />\n\n\tThe **time** is the micro timestamp used as the image name without the\n\tfile extension. It will be a number like this: 1139612155.3422\n\n\tThe **word** is the word that appears in the captcha image, which if not\n\tsupplied to the function, will be a random string.\n"
  },
  {
    "path": "user_guide_src/source/helpers/cookie_helper.rst",
    "content": "#############\nCookie Helper\n#############\n\nThe Cookie Helper file contains functions that assist in working with\ncookies.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\nLoading this Helper\n===================\n\nThis helper is loaded using the following code::\n\n\t$this->load->helper('cookie');\n\nAvailable Functions\n===================\n\nThe following functions are available:\n\n\n.. php:function:: set_cookie($name[, $value = ''[, $expire = 0[, $domain = ''[, $path = '/'[, $prefix = ''[, $secure = NULL[, $httponly = NULL[, $samesite = NULL]]]]]]]])\n\n\t:param\tmixed\t$name: Cookie name *or* associative array of all of the parameters available to this function\n\t:param\tstring\t$value: Cookie value\n\t:param\tint\t$expire: Number of seconds until expiration\n\t:param\tstring\t$domain: Cookie domain (usually: .yourdomain.com)\n\t:param\tstring\t$path: Cookie path\n\t:param\tstring\t$prefix: Cookie name prefix\n\t:param\tbool\t$secure: Whether to only send the cookie through HTTPS\n\t:param\tbool\t$httponly: Whether to hide the cookie from JavaScript\n\t:param\tstring\t$samesite: SameSite attribute ('Lax', 'Strict', 'None')\n\t:rtype:\tvoid\n\n\tThis helper function gives you friendlier syntax to set browser\n\tcookies. Refer to the :doc:`Input Library <../libraries/input>` for\n\ta description of its use, as this function is an alias for\n\t``CI_Input::set_cookie()``.\n\n.. php:function:: get_cookie($index[, $xss_clean = FALSE])\n\n\t:param\tstring\t$index: Cookie name\n\t:param\tbool\t$xss_clean: Whether to apply XSS filtering to the returned value\n\t:returns:\tThe cookie value or NULL if not found\n\t:rtype:\tmixed\n\n\tThis helper function gives you friendlier syntax to get browser\n\tcookies. Refer to the :doc:`Input Library <../libraries/input>` for\n\tdetailed description of its use, as this function acts very\n\tsimilarly to ``CI_Input::cookie()``, except it will also prepend\n\tthe ``$config['cookie_prefix']`` that you might've set in your\n\t*application/config/config.php* file.\n\n.. php:function:: delete_cookie($name[, $domain = ''[, $path = '/'[, $prefix = '']]])\n\n\t:param\tstring\t$name: Cookie name\n\t:param\tstring\t$domain: Cookie domain (usually: .yourdomain.com)\n\t:param\tstring\t$path: Cookie path\n\t:param\tstring\t$prefix: Cookie name prefix\n\t:rtype:\tvoid\n\n\tLets you delete a cookie. Unless you've set a custom path or other\n\tvalues, only the name of the cookie is needed.\n\t::\n\n\t\tdelete_cookie('name');\n\n\tThis function is otherwise identical to ``set_cookie()``, except that it\n\tdoes not have the value and expiration parameters. You can submit an\n\tarray of values in the first parameter or you can set discrete\n\tparameters.\n\t::\n\n\t\tdelete_cookie($name, $domain, $path, $prefix);\n"
  },
  {
    "path": "user_guide_src/source/helpers/date_helper.rst",
    "content": "###########\nDate Helper\n###########\n\nThe Date Helper file contains functions that help you work with dates.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\nLoading this Helper\n===================\n\nThis helper is loaded using the following code::\n\n\t$this->load->helper('date');\n\nAvailable Functions\n===================\n\nThe following functions are available:\n\n\n.. php:function:: now([$timezone = NULL])\n\n\t:param\tstring\t$timezone: Timezone\n\t:returns:\tUNIX timestamp\n\t:rtype:\tint\n\n\tReturns the current time as a UNIX timestamp, referenced either to your server's\n\tlocal time or any PHP supported timezone, based on the \"time reference\" setting\n\tin your config file. If you do not intend to set your master time reference to\n\tany other PHP supported timezone (which you'll typically do if you run a site\n\tthat lets each user set their own timezone settings) there is no benefit to using\n\tthis function over PHP's ``time()`` function.\n\t::\n\n\t\techo now('Australia/Victoria');\n\n\tIf a timezone is not provided, it will return ``time()`` based on the\n\t**time_reference** setting.\n\n.. php:function:: mdate([$datestr = ''[, $time = '']])\n\n\t:param\tstring\t$datestr: Date string\n\t:param\tint\t$time: UNIX timestamp\n\t:returns:\tMySQL-formatted date\n\t:rtype:\tstring\n\n\tThis function is identical to PHP's `date() <https://secure.php.net/manual/en/function.date.php>`_\n\tfunction, except that it lets you use MySQL style date codes, where each\n\tcode letter is preceded with a percent sign, e.g. `%Y %m %d`\n\n\tThe benefit of doing dates this way is that you don't have to worry\n\tabout escaping any characters that are not date codes, as you would\n\tnormally have to do with the ``date()`` function.\n\n\tExample::\n\n\t\t$datestring = 'Year: %Y Month: %m Day: %d - %h:%i %a';\n\t\t$time = time();\n\t\techo mdate($datestring, $time);\n\n\tIf a timestamp is not included in the second parameter the current time\n\twill be used.\n\n.. php:function:: local_to_gmt([$time = ''])\n\n\t:param\tint\t$time: UNIX timestamp\n\t:returns:\tUNIX timestamp\n\t:rtype:\tint\n\n\tTakes a UNIX timestamp as input and returns it as GMT.\n\n\tExample::\n\n\t\t$gmt = local_to_gmt(time());\n\n.. php:function:: gmt_to_local([$time = ''[, $timezone = 'UTC'[, $dst = FALSE]]])\n\n\t:param\tint\t$time: UNIX timestamp\n\t:param\tstring\t$timezone: Timezone\n\t:param\tbool\t$dst: Whether DST is active\n\t:returns:\tUNIX timestamp\n\t:rtype:\tint\n\n\tTakes a UNIX timestamp (referenced to GMT) as input, and converts it to\n\ta localized timestamp based on the timezone and Daylight Saving Time\n\tsubmitted.\n\n\tExample::\n\n\t\t$timestamp = 1140153693;\n\t\t$timezone  = 'UM8';\n\t\t$daylight_saving = TRUE;\n\t\techo gmt_to_local($timestamp, $timezone, $daylight_saving);\n\n\n\t.. note:: For a list of timezones see the reference at the bottom of this page.\n\n.. php:function:: mysql_to_unix([$time = ''])\n\n\t:param\tstring\t$time: MySQL timestamp\n\t:returns:\tUNIX timestamp\n\t:rtype:\tint\n\n\tTakes a MySQL Timestamp as input and returns it as a UNIX timestamp.\n\n\tExample::\n\n\t\t$unix = mysql_to_unix('20061124092345');\n\n.. php:function:: unix_to_human([$time = ''[, $seconds = FALSE[, $fmt = 'us']]])\n\n\t:param\tint\t$time: UNIX timestamp\n\t:param\tbool\t$seconds: Whether to show seconds\n\t:param\tstring\t$fmt: format (us or euro)\n\t:returns:\tFormatted date\n\t:rtype:\tstring\n\n\tTakes a UNIX timestamp as input and returns it in a human readable\n\tformat with this prototype::\n\n\t\tYYYY-MM-DD HH:MM:SS AM/PM\n\n\tThis can be useful if you need to display a date in a form field for\n\tsubmission.\n\n\tThe time can be formatted with or without seconds, and it can be set to\n\tEuropean or US format. If only the timestamp is submitted it will return\n\tthe time without seconds formatted for the U.S.\n\n\tExamples::\n\n\t\t$now = time();\n\t\techo unix_to_human($now); // U.S. time, no seconds\n\t\techo unix_to_human($now, TRUE, 'us'); // U.S. time with seconds\n\t\techo unix_to_human($now, TRUE, 'eu'); // Euro time with seconds\n\n.. php:function:: human_to_unix([$datestr = ''])\n\n\t:param\tint\t$datestr: Date string\n\t:returns:\tUNIX timestamp or FALSE on failure\n\t:rtype:\tint\n\n\tThe opposite of the :php:func:`unix_to_time()` function. Takes a \"human\"\n\ttime as input and returns it as a UNIX timestamp. This is useful if you\n\taccept \"human\" formatted dates submitted via a form. Returns boolean FALSE\n\tdate string passed to it is not formatted as indicated above.\n\n\tExample::\n\n\t\t$now = time();\n\t\t$human = unix_to_human($now);\n\t\t$unix = human_to_unix($human);\n\n.. php:function:: timespan([$seconds = 1[, $time = ''[, $units = '']]])\n\n\t:param\tint\t$seconds: Number of seconds\n\t:param\tstring\t$time: UNIX timestamp\n\t:param\tint\t$units: Number of time units to display\n\t:returns:\tFormatted time difference\n\t:rtype:\tstring\n\n\tFormats a UNIX timestamp so that is appears similar to this::\n\n\t\t1 Year, 10 Months, 2 Weeks, 5 Days, 10 Hours, 16 Minutes\n\n\tThe first parameter must contain a UNIX timestamp.\n\tThe second parameter must contain a timestamp that is greater that the\n\tfirst timestamp.\n\tThe thirdparameter is optional and limits the number of time units to display.\n\n\tIf the second parameter empty, the current time will be used.\n\n\tThe most common purpose for this function is to show how much time has\n\telapsed from some point in time in the past to now.\n\n\tExample::\n\n\t\t$post_date = '1079621429';\n\t\t$now = time();\n\t\t$units = 2;\n\t\techo timespan($post_date, $now, $units);\n\n\t.. note:: The text generated by this function is found in the following language\n\t\tfile: `language/<your_lang>/date_lang.php`\n\n.. php:function:: days_in_month([$month = 0[, $year = '']])\n\n\t:param\tint\t$month: a numeric month\n\t:param\tint\t$year: a numeric year\n\t:returns:\tCount of days in the specified month\n\t:rtype:\tint\n\n\tReturns the number of days in a given month/year. Takes leap years into\n\taccount.\n\n\tExample::\n\n\t\techo days_in_month(06, 2005);\n\n\tIf the second parameter is empty, the current year will be used.\n\n\t.. note:: This function will alias the native ``cal_days_in_month()``, if\n\t\tit is available.\n\n.. php:function:: date_range([$unix_start = ''[, $mixed = ''[, $is_unix = TRUE[, $format = 'Y-m-d']]]])\n\n\t:param\tint\t$unix_start: UNIX timestamp of the range start date\n\t:param\tint\t$mixed: UNIX timestamp of the range end date or interval in days\n\t:param\tbool\t$is_unix: set to FALSE if $mixed is not a timestamp\n\t:param\tstring\t$format: Output date format, same as in ``date()``\n\t:returns:\tAn array of dates\n\t:rtype:\tarray\n\n\tReturns a list of dates within a specified period.\n\n\tExample::\n\n\t\t$range = date_range('2012-01-01', '2012-01-15');\n\t\techo \"First 15 days of 2012:\";\n\t\tforeach ($range as $date)\n\t\t{\n\t\t\techo $date.\"\\n\";\n\t\t}\n\n.. php:function:: timezones([$tz = ''])\n\n\t:param\tstring\t$tz: A numeric timezone\n\t:returns:\tHour difference from UTC\n\t:rtype:\tint\n\n\tTakes a timezone reference (for a list of valid timezones, see the\n\t\"Timezone Reference\" below) and returns the number of hours offset from\n\tUTC.\n\n\tExample::\n\n\t\techo timezones('UM5');\n\n\n\tThis function is useful when used with :php:func:`timezone_menu()`.\n\n.. php:function:: timezone_menu([$default = 'UTC'[, $class = ''[, $name = 'timezones'[, $attributes = '']]]])\n\n\t:param\tstring\t$default: Timezone\n\t:param\tstring\t$class: Class name\n\t:param\tstring\t$name: Menu name\n\t:param\tmixed\t$attributes: HTML attributes\n\t:returns:\tHTML drop down menu with time zones\n\t:rtype:\tstring\n\n\tGenerates a pull-down menu of timezones, like this one:\n\n\t.. raw:: html\n\n\t\t<form action=\"#\">\n\t\t\t<select name=\"timezones\">\n\t\t\t\t<option value='UM12'>(UTC -12:00) Baker/Howland Island</option>\n\t\t\t\t<option value='UM11'>(UTC -11:00) Samoa Time Zone, Niue</option>\n\t\t\t\t<option value='UM10'>(UTC -10:00) Hawaii-Aleutian Standard Time, Cook Islands, Tahiti</option>\n\t\t\t\t<option value='UM95'>(UTC -9:30) Marquesas Islands</option>\n\t\t\t\t<option value='UM9'>(UTC -9:00) Alaska Standard Time, Gambier Islands</option>\n\t\t\t\t<option value='UM8'>(UTC -8:00) Pacific Standard Time, Clipperton Island</option>\n\t\t\t\t<option value='UM7'>(UTC -7:00) Mountain Standard Time</option>\n\t\t\t\t<option value='UM6'>(UTC -6:00) Central Standard Time</option>\n\t\t\t\t<option value='UM5'>(UTC -5:00) Eastern Standard Time, Western Caribbean Standard Time</option>\n\t\t\t\t<option value='UM45'>(UTC -4:30) Venezuelan Standard Time</option>\n\t\t\t\t<option value='UM4'>(UTC -4:00) Atlantic Standard Time, Eastern Caribbean Standard Time</option>\n\t\t\t\t<option value='UM35'>(UTC -3:30) Newfoundland Standard Time</option>\n\t\t\t\t<option value='UM3'>(UTC -3:00) Argentina, Brazil, French Guiana, Uruguay</option>\n\t\t\t\t<option value='UM2'>(UTC -2:00) South Georgia/South Sandwich Islands</option>\n\t\t\t\t<option value='UM1'>(UTC -1:00) Azores, Cape Verde Islands</option>\n\t\t\t\t<option value='UTC' selected='selected'>(UTC) Greenwich Mean Time, Western European Time</option>\n\t\t\t\t<option value='UP1'>(UTC +1:00) Central European Time, West Africa Time</option>\n\t\t\t\t<option value='UP2'>(UTC +2:00) Central Africa Time, Eastern European Time, Kaliningrad Time</option>\n\t\t\t\t<option value='UP3'>(UTC +3:00) Moscow Time, East Africa Time</option>\n\t\t\t\t<option value='UP35'>(UTC +3:30) Iran Standard Time</option>\n\t\t\t\t<option value='UP4'>(UTC +4:00) Azerbaijan Standard Time, Samara Time</option>\n\t\t\t\t<option value='UP45'>(UTC +4:30) Afghanistan</option>\n\t\t\t\t<option value='UP5'>(UTC +5:00) Pakistan Standard Time, Yekaterinburg Time</option>\n\t\t\t\t<option value='UP55'>(UTC +5:30) Indian Standard Time, Sri Lanka Time</option>\n\t\t\t\t<option value='UP575'>(UTC +5:45) Nepal Time</option>\n\t\t\t\t<option value='UP6'>(UTC +6:00) Bangladesh Standard Time, Bhutan Time, Omsk Time</option>\n\t\t\t\t<option value='UP65'>(UTC +6:30) Cocos Islands, Myanmar</option>\n\t\t\t\t<option value='UP7'>(UTC +7:00) Krasnoyarsk Time, Cambodia, Laos, Thailand, Vietnam</option>\n\t\t\t\t<option value='UP8'>(UTC +8:00) Australian Western Standard Time, Beijing Time, Irkutsk Time</option>\n\t\t\t\t<option value='UP875'>(UTC +8:45) Australian Central Western Standard Time</option>\n\t\t\t\t<option value='UP9'>(UTC +9:00) Japan Standard Time, Korea Standard Time, Yakutsk Time</option>\n\t\t\t\t<option value='UP95'>(UTC +9:30) Australian Central Standard Time</option>\n\t\t\t\t<option value='UP10'>(UTC +10:00) Australian Eastern Standard Time, Vladivostok Time</option>\n\t\t\t\t<option value='UP105'>(UTC +10:30) Lord Howe Island</option>\n\t\t\t\t<option value='UP11'>(UTC +11:00) Srednekolymsk Time, Solomon Islands, Vanuatu</option>\n\t\t\t\t<option value='UP115'>(UTC +11:30) Norfolk Island</option>\n\t\t\t\t<option value='UP12'>(UTC +12:00) Fiji, Gilbert Islands, Kamchatka Time, New Zealand Standard Time</option>\n\t\t\t\t<option value='UP1275'>(UTC +12:45) Chatham Islands Standard Time</option>\n\t\t\t\t<option value='UP13'>(UTC +13:00) Phoenix Islands Time, Tonga</option>\n\t\t\t\t<option value='UP14'>(UTC +14:00) Line Islands</option>\n\t\t\t</select>\n\t\t</form>\n\n\n\tThis menu is useful if you run a membership site in which your users are\n\tallowed to set their local timezone value.\n\n\tThe first parameter lets you set the \"selected\" state of the menu. For\n\texample, to set Pacific time as the default you will do this::\n\n\t\techo timezone_menu('UM8');\n\n\tPlease see the timezone reference below to see the values of this menu.\n\n\tThe second parameter lets you set a CSS class name for the menu.\n\n\tThe fourth parameter lets you set one or more attributes on the generated select tag.\n\n\t.. note:: The text contained in the menu is found in the following\n\t\tlanguage file: `language/<your_lang>/date_lang.php`\n\nTimezone Reference\n==================\n\nThe following table indicates each timezone and its location.\n\nNote some of the location lists have been abridged for clarity and formatting.\n\n===========     =====================================================================\nTime Zone       Location\n===========     =====================================================================\nUM12            (UTC - 12:00) Baker/Howland Island\nUM11            (UTC - 11:00) Samoa Time Zone, Niue\nUM10            (UTC - 10:00) Hawaii-Aleutian Standard Time, Cook Islands\nUM95            (UTC - 09:30) Marquesas Islands\nUM9             (UTC - 09:00) Alaska Standard Time, Gambier Islands\nUM8             (UTC - 08:00) Pacific Standard Time, Clipperton Island\nUM7             (UTC - 07:00) Mountain Standard Time\nUM6             (UTC - 06:00) Central Standard Time\nUM5             (UTC - 05:00) Eastern Standard Time, Western Caribbean\nUM45            (UTC - 04:30) Venezuelan Standard Time\nUM4             (UTC - 04:00) Atlantic Standard Time, Eastern Caribbean\nUM35            (UTC - 03:30) Newfoundland Standard Time\nUM3             (UTC - 03:00) Argentina, Brazil, French Guiana, Uruguay\nUM2             (UTC - 02:00) South Georgia/South Sandwich Islands\nUM1             (UTC -1:00) Azores, Cape Verde Islands\nUTC             (UTC) Greenwich Mean Time, Western European Time\nUP1             (UTC +1:00) Central European Time, West Africa Time\nUP2             (UTC +2:00) Central Africa Time, Eastern European Time\nUP3             (UTC +3:00) Moscow Time, East Africa Time\nUP35            (UTC +3:30) Iran Standard Time\nUP4             (UTC +4:00) Azerbaijan Standard Time, Samara Time\nUP45            (UTC +4:30) Afghanistan\nUP5             (UTC +5:00) Pakistan Standard Time, Yekaterinburg Time\nUP55            (UTC +5:30) Indian Standard Time, Sri Lanka Time\nUP575           (UTC +5:45) Nepal Time\nUP6             (UTC +6:00) Bangladesh Standard Time, Bhutan Time, Omsk Time\nUP65            (UTC +6:30) Cocos Islands, Myanmar\nUP7             (UTC +7:00) Krasnoyarsk Time, Cambodia, Laos, Thailand, Vietnam\nUP8             (UTC +8:00) Australian Western Standard Time, Beijing Time\nUP875           (UTC +8:45) Australian Central Western Standard Time\nUP9             (UTC +9:00) Japan Standard Time, Korea Standard Time, Yakutsk\nUP95            (UTC +9:30) Australian Central Standard Time\nUP10            (UTC +10:00) Australian Eastern Standard Time, Vladivostok Time\nUP105           (UTC +10:30) Lord Howe Island\nUP11            (UTC +11:00) Srednekolymsk Time, Solomon Islands, Vanuatu\nUP115           (UTC +11:30) Norfolk Island\nUP12            (UTC +12:00) Fiji, Gilbert Islands, Kamchatka, New Zealand\nUP1275          (UTC +12:45) Chatham Islands Standard Time\nUP13            (UTC +13:00) Phoenix Islands Time, Tonga\nUP14            (UTC +14:00) Line Islands\n===========\t=====================================================================\n"
  },
  {
    "path": "user_guide_src/source/helpers/directory_helper.rst",
    "content": "################\nDirectory Helper\n################\n\nThe Directory Helper file contains functions that assist in working with\ndirectories.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\nLoading this Helper\n===================\n\nThis helper is loaded using the following code:\n\n::\n\n\t$this->load->helper('directory');\n\nAvailable Functions\n===================\n\nThe following functions are available:\n\n\n.. php:function:: directory_map($source_dir[, $directory_depth = 0[, $hidden = FALSE]])\n\n\t:param\tstring\t$source_dir: Path to the source directory\n\t:param\tint\t$directory_depth: Depth of directories to traverse (0 = fully recursive, 1 = current dir, etc)\n\t:param\tbool\t$hidden: Whether to include hidden directories\n\t:returns:\tAn array of files\n\t:rtype:\tarray\n\n\tExamples::\n\n\t\t$map = directory_map('./mydirectory/');\n\n\t.. note:: Paths are almost always relative to your main index.php file.\n\n\n\tSub-folders contained within the directory will be mapped as well. If\n\tyou wish to control the recursion depth, you can do so using the second\n\tparameter (integer). A depth of 1 will only map the top level directory::\n\n\t\t$map = directory_map('./mydirectory/', 1);\n\n\tBy default, hidden files will not be included in the returned array. To\n\toverride this behavior, you may set a third parameter to true (boolean)::\n\n\t\t$map = directory_map('./mydirectory/', FALSE, TRUE);\n\n\tEach folder name will be an array index, while its contained files will\n\tbe numerically indexed. Here is an example of a typical array::\n\n\t\tArray (\n\t\t\t[libraries] => Array\n\t\t\t\t(\n\t\t\t\t\t[0] => benchmark.html\n\t\t\t\t\t[1] => config.html\n\t\t\t\t\t[\"database/\"] => Array\n\t\t\t\t\t\t(\n\t\t\t\t\t\t\t[0] => query_builder.html\n\t\t\t\t\t\t\t[1] => binds.html\n\t\t\t\t\t\t\t[2] => configuration.html\n\t\t\t\t\t\t\t[3] => connecting.html\n\t\t\t\t\t\t\t[4] => examples.html\n\t\t\t\t\t\t\t[5] => fields.html\n\t\t\t\t\t\t\t[6] => index.html\n\t\t\t\t\t\t\t[7] => queries.html\n\t\t\t\t\t\t)\n\t\t\t\t\t[2] => email.html\n\t\t\t\t\t[3] => file_uploading.html\n\t\t\t\t\t[4] => image_lib.html\n\t\t\t\t\t[5] => input.html\n\t\t\t\t\t[6] => language.html\n\t\t\t\t\t[7] => loader.html\n\t\t\t\t\t[8] => pagination.html\n\t\t\t\t\t[9] => uri.html\n\t\t\t\t)"
  },
  {
    "path": "user_guide_src/source/helpers/download_helper.rst",
    "content": "###############\nDownload Helper\n###############\n\nThe Download Helper lets you download data to your desktop.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\nLoading this Helper\n===================\n\nThis helper is loaded using the following code::\n\n\t$this->load->helper('download');\n\nAvailable Functions\n===================\n\nThe following functions are available:\n\n\n.. php:function:: force_download([$filename = ''[, $data = ''[, $set_mime = FALSE]]])\n\n\t:param\tmixed\t$filename: Filename\n\t:param\tmixed\t$data: File contents\n\t:param\tbool\t$set_mime: Whether to try to send the actual MIME type\n\t:rtype:\tvoid\n\n\tGenerates server headers which force data to be downloaded to your\n\tdesktop. Useful with file downloads. The first parameter is the **name\n\tyou want the downloaded file to be named**, the second parameter is the\n\tfile data.\n\n\tIf you set the second parameter to NULL and ``$filename`` is an existing, readable\n\tfile path, then its content will be read instead. You may also set ``$filename``\n\tas an associative array with a single element, where the key of that element would be \n\tthe local file you are trying to read and where the value is the name of the downloadable\n\tfile that will be sent to browser. An example of this is provided below.\n\t\n\tIf you set the third parameter to boolean TRUE, then the actual file MIME type\n\t(based on the filename extension) will be sent, so that if your browser has a\n\thandler for that type - it can use it.\n\n\tExample::\n\n\t\t$data = 'Here is some text!';\n\t\t$name = 'mytext.txt';\n\t\tforce_download($name, $data);\n\n\tIf you want to download an existing file from your server you'll need to\n\tdo the following::\n\n\t\t// Contents of photo.jpg will be automatically read\n\t\tforce_download('/path/to/photo.jpg', NULL);\n\n\tIf you want to download an existing file from your server, but change the name\n\tof the actual file sent to browser, you will need this::\n\n\t\t// Contents of photo.jpg will be automatically read and sent as my-photo.jpg\n\t\tforce_download(array('/path/to/photo.jpg' => 'my-photo.jpg'), NULL);"
  },
  {
    "path": "user_guide_src/source/helpers/file_helper.rst",
    "content": "###########\nFile Helper\n###########\n\nThe File Helper file contains functions that assist in working with files.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\nLoading this Helper\n===================\n\nThis helper is loaded using the following code::\n\n\t$this->load->helper('file');\n\nAvailable Functions\n===================\n\nThe following functions are available:\n\n.. php:function:: write_file($path, $data[, $mode = 'wb'])\n\n\t:param\tstring\t$path: File path\n\t:param\tstring\t$data: Data to write to file\n\t:param\tstring\t$mode: ``fopen()`` mode\n\t:returns:\tTRUE if the write was successful, FALSE in case of an error\n\t:rtype:\tbool\n\n\tWrites data to the file specified in the path. If the file does not exist then the\n\tfunction will create it.\n\n\tExample::\n\n\t\t$data = 'Some file data';\n\t\tif ( ! write_file('./path/to/file.php', $data))\n\t\t{     \n\t\t\techo 'Unable to write the file';\n\t\t}\n\t\telse\n\t\t{     \n\t\t\techo 'File written!';\n\t\t}\n\n\tYou can optionally set the write mode via the third parameter::\n\n\t\twrite_file('./path/to/file.php', $data, 'r+');\n\n\tThe default mode is 'wb'. Please see the `PHP user guide <https://secure.php.net/manual/en/function.fopen.php>`_\n\tfor mode options.\n\n\t.. note: In order for this function to write data to a file, its permissions must\n\t\tbe set such that it is writable. If the file does not already exist,\n\t\tthen the directory containing it must be writable.\n\n\t.. note:: The path is relative to your main site index.php file, NOT your\n\t\tcontroller or view files. CodeIgniter uses a front controller so paths\n\t\tare always relative to the main site index.\n\n\t.. note:: This function acquires an exclusive lock on the file while writing to it.\n\n.. php:function:: delete_files($path[, $del_dir = FALSE[, $htdocs = FALSE]])\n\n\t:param\tstring\t$path: Directory path\n\t:param\tbool\t$del_dir: Whether to also delete directories\n\t:param\tbool\t$htdocs: Whether to skip deleting .htaccess and index page files\n\t:returns:\tTRUE on success, FALSE in case of an error\n\t:rtype:\tbool\n\n\tDeletes ALL files contained in the supplied path.\n\n\tExample::\n\n\t\tdelete_files('./path/to/directory/');\n\n\tIf the second parameter is set to TRUE, any directories contained within the supplied\n\troot path will be deleted as well.\n\n\tExample::\n\n\t\tdelete_files('./path/to/directory/', TRUE);\n\n\t.. note:: The files must be writable or owned by the system in order to be deleted.\n\n.. php:function:: get_filenames($source_dir[, $include_path = FALSE])\n\n\t:param\tstring\t$source_dir: Directory path\n\t:param\tbool\t$include_path: Whether to include the path as part of the filenames\n\t:returns:\tAn array of file names\n\t:rtype:\tarray\n\n\tTakes a server path as input and returns an array containing the names of all files\n\tcontained within it. The file path can optionally be added to the file names by setting\n\tthe second parameter to TRUE.\n\n\tExample::\n\n\t\t$controllers = get_filenames(APPPATH.'controllers/');\n\n.. php:function:: get_dir_file_info($source_dir, $top_level_only)\n\n\t:param\tstring\t$source_dir: Directory path\n\t:param\tbool\t$top_level_only: Whether to look only at the specified directory (excluding sub-directories)\n\t:returns:\tAn array containing info on the supplied directory's contents\n\t:rtype:\tarray\n\n\tReads the specified directory and builds an array containing the filenames, filesize,\n\tdates, and permissions. Sub-folders contained within the specified path are only read\n\tif forced by sending the second parameter to FALSE, as this can be an intensive\n\toperation.\n\n\tExample::\n\n\t\t$models_info = get_dir_file_info(APPPATH.'models/');\n\n.. php:function:: get_file_info($file[, $returned_values = array('name', 'server_path', 'size', 'date')])\n\n\t:param\tstring\t$file: File path\n\t:param\tarray\t$returned_values: What type of info to return\n\t:returns:\tAn array containing info on the specified file or FALSE on failure\n\t:rtype:\tarray\n\n\tGiven a file and path, returns (optionally) the *name*, *path*, *size* and *date modified*\n\tinformation attributes for a file. Second parameter allows you to explicitly declare what\n\tinformation you want returned.\n\n\tValid ``$returned_values`` options are: `name`, `size`, `date`, `readable`, `writeable`,\n\t`executable` and `fileperms`.\n\n.. php:function:: get_mime_by_extension($filename)\n\n\t:param\tstring\t$filename: File name\n\t:returns:\tMIME type string or FALSE on failure\n\t:rtype:\tstring\n\n\tTranslates a filename extension into a MIME type based on *config/mimes.php*.\n\tReturns FALSE if it can't determine the type, or read the MIME config file.\n\n\t::\n\n\t\t$file = 'somefile.png';\n\t\techo $file.' is has a mime type of '.get_mime_by_extension($file);\n\n\t.. note:: This is not an accurate way of determining file MIME types, and\n\t\tis here strictly for convenience. It should not be used for security\n\t\tpurposes.\n\n.. php:function:: symbolic_permissions($perms)\n\n\t:param\tint\t$perms: Permissions\n\t:returns:\tSymbolic permissions string\n\t:rtype:\tstring\n\n\tTakes numeric permissions (such as is returned by ``fileperms()``) and returns\n\tstandard symbolic notation of file permissions.\n\n\t::\n\n\t\techo symbolic_permissions(fileperms('./index.php'));  // -rw-r--r--\n\n.. php:function:: octal_permissions($perms)\n\n\t:param\tint\t$perms: Permissions\n\t:returns:\tOctal permissions string\n\t:rtype:\tstring\n\n\tTakes numeric permissions (such as is returned by ``fileperms()``) and returns\n\ta three character octal notation of file permissions.\n\n\t::\n\n\t\techo octal_permissions(fileperms('./index.php')); // 644\n"
  },
  {
    "path": "user_guide_src/source/helpers/form_helper.rst",
    "content": "###########\nForm Helper\n###########\n\nThe Form Helper file contains functions that assist in working with\nforms.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\nLoading this Helper\n===================\n\nThis helper is loaded using the following code::\n\n\t$this->load->helper('form');\n\nEscaping field values\n=====================\n\nYou may need to use HTML and characters such as quotes within your form\nelements. In order to do that safely, you'll need to use\n:doc:`common function <../general/common_functions>`\n:func:`html_escape()`.\n\nConsider the following example::\n\n\t$string = 'Here is a string containing \"quoted\" text.';\n\n\t<input type=\"text\" name=\"myfield\" value=\"<?php echo $string; ?>\" />\n\nSince the above string contains a set of quotes, it will cause the form\nto break. The :php:func:`html_escape()` function converts HTML special\ncharacters so that it can be used safely::\n\n\t<input type=\"text\" name=\"myfield\" value=\"<?php echo html_escape($string); ?>\" />\n\n.. note:: If you use any of the form helper functions listed on this page,\n\tthe form values will be automatically escaped, so there is no need\n\tto call this function. Use it only if you are creating your own\n\tform elements.\n\nAvailable Functions\n===================\n\nThe following functions are available:\n\n\n.. php:function:: form_open([$action = ''[, $attributes = ''[, $hidden = array()]]])\n\n\t:param\tstring\t$action: Form action/target URI string\n\t:param\tarray\t$attributes: HTML attributes\n\t:param\tarray\t$hidden: An array of hidden fields' definitions\n\t:returns:\tAn HTML form opening tag\n\t:rtype:\tstring\n\n\tCreates an opening form tag with a base URL **built from your config preferences**.\n\tIt will optionally let you add form attributes and hidden input fields, and\n\twill always add the `accept-charset` attribute based on the charset value in your\n\tconfig file.\n\n\tThe main benefit of using this tag rather than hard coding your own HTML is that\n\tit permits your site to be more portable in the event your URLs ever change.\n\n\tHere's a simple example::\n\n\t\techo form_open('email/send');\n\n\tThe above example would create a form that points to your base URL plus the\n\t\"email/send\" URI segments, like this::\n\n\t\t<form method=\"post\" accept-charset=\"utf-8\" action=\"http://example.com/index.php/email/send\">\n\n\t**Adding Attributes**\n\n\t\tAttributes can be added by passing an associative array to the second\n\t\tparameter, like this::\n\n\t\t\t$attributes = array('class' => 'email', 'id' => 'myform');\n\t\t\techo form_open('email/send', $attributes);\n\n\t\tAlternatively, you can specify the second parameter as a string::\n\n\t\t\techo form_open('email/send', 'class=\"email\" id=\"myform\"');\n\n\t\tThe above examples would create a form similar to this::\n\n\t\t\t<form method=\"post\" accept-charset=\"utf-8\" action=\"http://example.com/index.php/email/send\" class=\"email\" id=\"myform\">\n\n\t**Adding Hidden Input Fields**\n\n\t\tHidden fields can be added by passing an associative array to the\n\t\tthird parameter, like this::\n\n\t\t\t$hidden = array('username' => 'Joe', 'member_id' => '234');\n\t\t\techo form_open('email/send', '', $hidden);\n\n\t\tYou can skip the second parameter by passing any falsy value to it.\n\n\t\tThe above example would create a form similar to this::\n\n\t\t\t<form method=\"post\" accept-charset=\"utf-8\" action=\"http://example.com/index.php/email/send\">\n\t\t\t\t<input type=\"hidden\" name=\"username\" value=\"Joe\" />\n\t\t\t\t<input type=\"hidden\" name=\"member_id\" value=\"234\" />\n\n\n.. php:function:: form_open_multipart([$action = ''[, $attributes = array()[, $hidden = array()]]])\n\n\t:param\tstring\t$action: Form action/target URI string\n\t:param\tarray\t$attributes: HTML attributes\n\t:param\tarray\t$hidden: An array of hidden fields' definitions\n\t:returns:\tAn HTML multipart form opening tag\n\t:rtype:\tstring\n\n\tThis function is absolutely identical to :php:func:`form_open()` above,\n\texcept that it adds a *multipart* attribute, which is necessary if you\n\twould like to use the form to upload files with.\n\n\n.. php:function:: form_hidden($name[, $value = ''])\n\n\t:param\tstring\t$name: Field name\n\t:param\tstring\t$value: Field value\n\t:returns:\tAn HTML hidden input field tag\n\t:rtype:\tstring\n\n\tLets you generate hidden input fields. You can either submit a\n\tname/value string to create one field::\n\n\t\tform_hidden('username', 'johndoe');\n\t\t// Would produce: <input type=\"hidden\" name=\"username\" value=\"johndoe\" />\n\n\t... or you can submit an associative array to create multiple fields::\n\n\t\t$data = array(\n\t\t\t'name'\t=> 'John Doe',\n\t\t\t'email'\t=> 'john@example.com',\n\t\t\t'url'\t=> 'http://example.com'\n\t\t);\n\n\t\techo form_hidden($data);\n\n\t\t/*\n\t\t\tWould produce:\n\t\t\t<input type=\"hidden\" name=\"name\" value=\"John Doe\" />\n\t\t\t<input type=\"hidden\" name=\"email\" value=\"john@example.com\" />\n\t\t\t<input type=\"hidden\" name=\"url\" value=\"http://example.com\" />\n\t\t*/\n\n\tYou can also pass an associative array to the value field::\n\n\t\t$data = array(\n\t\t\t'name'\t=> 'John Doe',\n\t\t\t'email'\t=> 'john@example.com',\n\t\t\t'url'\t=> 'http://example.com'\n\t\t);\n\n\t\techo form_hidden('my_array', $data);\n\n\t\t/*\n\t\t\tWould produce:\n\n\t\t\t<input type=\"hidden\" name=\"my_array[name]\" value=\"John Doe\" />\n\t\t\t<input type=\"hidden\" name=\"my_array[email]\" value=\"john@example.com\" />\n\t\t\t<input type=\"hidden\" name=\"my_array[url]\" value=\"http://example.com\" />\n\t\t*/\n\n\tIf you want to create hidden input fields with extra attributes::\n\n\t\t$data = array(\n\t\t\t'type'\t=> 'hidden',\n\t\t\t'name'\t=> 'email',\n\t\t\t'id'\t=> 'hiddenemail',\n\t\t\t'value'\t=> 'john@example.com',\n\t\t\t'class'\t=> 'hiddenemail'\n\t\t);\n\n\t\techo form_input($data);\n\n\t\t/*\n\t\t\tWould produce:\n\n\t\t\t<input type=\"hidden\" name=\"email\" value=\"john@example.com\" id=\"hiddenemail\" class=\"hiddenemail\" />\n\t\t*/\n\n.. php:function:: form_input([$data = ''[, $value = ''[, $extra = '']]])\n\n\t:param\tarray\t$data: Field attributes data\n\t:param\tstring\t$value: Field value\n\t:param\tmixed\t$extra: Extra attributes to be added to the tag either as an array or a literal string\n\t:returns:\tAn HTML text input field tag\n\t:rtype:\tstring\n\n\tLets you generate a standard text input field. You can minimally pass\n\tthe field name and value in the first and second parameter::\n\n\t\techo form_input('username', 'johndoe');\n\n\tOr you can pass an associative array containing any data you wish your\n\tform to contain::\n\n\t\t$data = array(\n\t\t\t'name'\t\t=> 'username',\n\t\t\t'id'\t\t=> 'username',\n\t\t\t'value'\t\t=> 'johndoe',\n\t\t\t'maxlength'\t=> '100',\n\t\t\t'size'\t\t=> '50',\n\t\t\t'style'\t\t=> 'width:50%'\n\t\t);\n\n\t\techo form_input($data);\n\n\t\t/*\n\t\t\tWould produce:\n\n\t\t\t<input type=\"text\" name=\"username\" value=\"johndoe\" id=\"username\" maxlength=\"100\" size=\"50\" style=\"width:50%\"  />\n\t\t*/\n\n\tIf you would like your form to contain some additional data, like\n\tJavaScript, you can pass it as a string in the third parameter::\n\n\t\t$js = 'onClick=\"some_function()\"';\n\t\techo form_input('username', 'johndoe', $js);\n\n\tOr you can pass it as an array::\n\n\t\t$js = array('onClick' => 'some_function();');\n\t\techo form_input('username', 'johndoe', $js);\n\n.. php:function:: form_password([$data = ''[, $value = ''[, $extra = '']]])\n\n\t:param\tarray\t$data: Field attributes data\n\t:param\tstring\t$value: Field value\n\t:param\tmixed\t$extra: Extra attributes to be added to the tag either as an array or a literal string\n\t:returns:\tAn HTML password input field tag\n\t:rtype:\tstring\n\n\tThis function is identical in all respects to the :php:func:`form_input()`\n\tfunction above except that it uses the \"password\" input type.\n\n\n.. php:function:: form_upload([$data = '', $extra = '']])\n\n\t:param\tarray\t$data: Field attributes data\n\t:param\tmixed\t$extra: Extra attributes to be added to the tag either as an array or a literal string\n\t:returns:\tAn HTML file upload input field tag\n\t:rtype:\tstring\n\n\tThis function is identical in all respects to the :php:func:`form_input()`\n\tfunction above except that it uses the \"file\" input type, allowing it to\n\tbe used to upload files.\n\n\n.. php:function:: form_textarea([$data = ''[, $value = ''[, $extra = '']]])\n\n\t:param\tarray\t$data: Field attributes data\n\t:param\tstring\t$value: Field value\n\t:param\tmixed\t$extra: Extra attributes to be added to the tag either as an array or a literal string\n\t:returns:\tAn HTML textarea tag\n\t:rtype:\tstring\n\n\tThis function is identical in all respects to the :php:func:`form_input()`\n\tfunction above except that it generates a \"textarea\" type.\n\n\t.. note:: Instead of the *maxlength* and *size* attributes in the above example,\n\t\tyou will instead specify *rows* and *cols*.\n\n.. php:function:: form_dropdown([$name = ''[, $options = array()[, $selected = array()[, $extra = '']]]])\n\n\t:param\tstring\t$name: Field name\n\t:param\tarray\t$options: An associative array of options to be listed\n\t:param\tarray\t$selected: List of fields to mark with the *selected* attribute\n\t:param\tmixed\t$extra: Extra attributes to be added to the tag either as an array or a literal string\n\t:returns:\tAn HTML dropdown select field tag\n\t:rtype:\tstring\n\n\tLets you create a standard drop-down field. The first parameter will\n\tcontain the name of the field, the second parameter will contain an\n\tassociative array of options, and the third parameter will contain the\n\tvalue you wish to be selected. You can also pass an array of multiple\n\titems through the third parameter, and CodeIgniter will create a\n\tmultiple select for you.\n\n\tExample::\n\n\t\t$options = array(\n\t\t\t'small'\t\t=> 'Small Shirt',\n\t\t\t'med'\t\t=> 'Medium Shirt',\n\t\t\t'large'\t\t=> 'Large Shirt',\n\t\t\t'xlarge'\t=> 'Extra Large Shirt',\n\t\t);\n\n\t\t$shirts_on_sale = array('small', 'large');\n\t\techo form_dropdown('shirts', $options, 'large');\n\n\t\t/*\n\t\t\tWould produce:\n\n\t\t\t<select name=\"shirts\">\n\t\t\t\t<option value=\"small\">Small Shirt</option>\n\t\t\t\t<option value=\"med\">Medium  Shirt</option>\n\t\t\t\t<option value=\"large\" selected=\"selected\">Large Shirt</option>\n\t\t\t\t<option value=\"xlarge\">Extra Large Shirt</option>\n\t\t\t</select>\n\t\t*/\n\n\t\techo form_dropdown('shirts', $options, $shirts_on_sale);\n\n\t\t/*\n\t\t\tWould produce:\n\n\t\t\t<select name=\"shirts\" multiple=\"multiple\">\n\t\t\t\t<option value=\"small\" selected=\"selected\">Small Shirt</option>\n\t\t\t\t<option value=\"med\">Medium  Shirt</option>\n\t\t\t\t<option value=\"large\" selected=\"selected\">Large Shirt</option>\n\t\t\t\t<option value=\"xlarge\">Extra Large Shirt</option>\n\t\t\t</select>\n\t\t*/\n\n\tIf you would like the opening <select> to contain additional data, like\n\tan id attribute or JavaScript, you can pass it as a string in the fourth\n\tparameter::\n\n\t\t$js = 'id=\"shirts\" onChange=\"some_function();\"';\n\t\techo form_dropdown('shirts', $options, 'large', $js);\n\n\tOr you can pass it as an array::\n\n\t\t$js = array(\n\t\t\t'id'       => 'shirts',\n\t\t\t'onChange' => 'some_function();'\n\t\t);\n\t\techo form_dropdown('shirts', $options, 'large', $js);\n\n\tIf the array passed as ``$options`` is a multidimensional array, then\n\t``form_dropdown()`` will produce an <optgroup> with the array key as the\n\tlabel.\n\n\n.. php:function:: form_multiselect([$name = ''[, $options = array()[, $selected = array()[, $extra = '']]]])\n\n\t:param\tstring\t$name: Field name\n\t:param\tarray\t$options: An associative array of options to be listed\n\t:param\tarray\t$selected: List of fields to mark with the *selected* attribute\n\t:param\tmixed\t$extra: Extra attributes to be added to the tag either as an array or a literal string\n\t:returns:\tAn HTML dropdown multiselect field tag\n\t:rtype:\tstring\n\n\tLets you create a standard multiselect field. The first parameter will\n\tcontain the name of the field, the second parameter will contain an\n\tassociative array of options, and the third parameter will contain the\n\tvalue or values you wish to be selected.\n\n\tThe parameter usage is identical to using :php:func:`form_dropdown()` above,\n\texcept of course that the name of the field will need to use POST array\n\tsyntax, e.g. foo[].\n\n\n.. php:function:: form_fieldset([$legend_text = ''[, $attributes = array()]])\n\n\t:param\tstring\t$legend_text: Text to put in the <legend> tag\n\t:param\tarray\t$attributes: Attributes to be set on the <fieldset> tag\n\t:returns:\tAn HTML fieldset opening tag\n\t:rtype:\tstring\n\n\tLets you generate fieldset/legend fields.\n\n\tExample::\n\n\t\techo form_fieldset('Address Information');\n\t\techo \"<p>fieldset content here</p>\\n\";\n\t\techo form_fieldset_close();\n\n\t\t/*\n\t\t\tProduces:\n\n\t\t\t\t<fieldset>\n\t\t\t\t\t<legend>Address Information</legend>\n\t\t\t\t\t\t<p>fieldset content here</p>\n\t\t\t\t</fieldset>\n\t\t*/\n\n\tSimilar to other functions, you can submit an associative array in the\n\tsecond parameter if you prefer to set additional attributes::\n\n\t\t$attributes = array(\n\t\t\t'id'\t=> 'address_info',\n\t\t\t'class'\t=> 'address_info'\n\t\t);\n\n\t\techo form_fieldset('Address Information', $attributes);\n\t\techo \"<p>fieldset content here</p>\\n\";\n\t\techo form_fieldset_close();\n\n\t\t/*\n\t\t\tProduces:\n\n\t\t\t<fieldset id=\"address_info\" class=\"address_info\">\n\t\t\t\t<legend>Address Information</legend>\n\t\t\t\t<p>fieldset content here</p>\n\t\t\t</fieldset>\n\t\t*/\n\n\n.. php:function:: form_fieldset_close([$extra = ''])\n\n\t:param\tstring\t$extra: Anything to append after the closing tag, *as is*\n\t:returns:\tAn HTML fieldset closing tag\n\t:rtype:\tstring\n\t\n\n\tProduces a closing </fieldset> tag. The only advantage to using this\n\tfunction is it permits you to pass data to it which will be added below\n\tthe tag. For example\n\n\t::\n\n\t\t$string = '</div></div>';\n\t\techo form_fieldset_close($string);\n\t\t// Would produce: </fieldset></div></div>\n\n\n.. php:function:: form_checkbox([$data = ''[, $value = ''[, $checked = FALSE[, $extra = '']]]])\n\n\t:param\tarray\t$data: Field attributes data\n\t:param\tstring\t$value: Field value\n\t:param\tbool\t$checked: Whether to mark the checkbox as being *checked*\n\t:param\tmixed\t$extra: Extra attributes to be added to the tag either as an array or a literal string\n\t:returns:\tAn HTML checkbox input tag\n\t:rtype:\tstring\n\n\tLets you generate a checkbox field. Simple example::\n\n\t\techo form_checkbox('newsletter', 'accept', TRUE);\n\t\t// Would produce:  <input type=\"checkbox\" name=\"newsletter\" value=\"accept\" checked=\"checked\" />\n\n\tThe third parameter contains a boolean TRUE/FALSE to determine whether\n\tthe box should be checked or not.\n\n\tSimilar to the other form functions in this helper, you can also pass an\n\tarray of attributes to the function::\n\n\t\t$data = array(\n\t\t\t'name'\t\t=> 'newsletter',\n\t\t\t'id'\t\t=> 'newsletter',\n\t\t\t'value'\t\t=> 'accept',\n\t\t\t'checked'\t=> TRUE,\n\t\t\t'style'\t\t=> 'margin:10px'\n\t\t);\n\n\t\techo form_checkbox($data);\n\t\t// Would produce: <input type=\"checkbox\" name=\"newsletter\" id=\"newsletter\" value=\"accept\" checked=\"checked\" style=\"margin:10px\" />\n\n\tAlso as with other functions, if you would like the tag to contain\n\tadditional data like JavaScript, you can pass it as a string in the\n\tfourth parameter::\n\n\t\t$js = 'onClick=\"some_function()\"';\n\t\techo form_checkbox('newsletter', 'accept', TRUE, $js);\n\n\tOr you can pass it as an array::\n\n\t\t$js = array('onClick' => 'some_function();');\n\t\techo form_checkbox('newsletter', 'accept', TRUE, $js);\n\n\n.. php:function:: form_radio([$data = ''[, $value = ''[, $checked = FALSE[, $extra = '']]]])\n\n\t:param\tarray\t$data: Field attributes data\n\t:param\tstring\t$value: Field value\n\t:param\tbool\t$checked: Whether to mark the radio button as being *checked*\n\t:param\tmixed\t$extra: Extra attributes to be added to the tag either as an array or a literal string\n\t:returns:\tAn HTML radio input tag\n\t:rtype:\tstring\n\n\tThis function is identical in all respects to the :php:func:`form_checkbox()`\n\tfunction above except that it uses the \"radio\" input type.\n\n\n.. php:function:: form_label([$label_text = ''[, $id = ''[, $attributes = array()]]])\n\n\t:param\tstring\t$label_text: Text to put in the <label> tag\n\t:param\tstring\t$id: ID of the form element that we're making a label for\n\t:param\tmixed\t$attributes: HTML attributes\n\t:returns:\tAn HTML field label tag\n\t:rtype:\tstring\n\n\tLets you generate a <label>. Simple example::\n\n\t\techo form_label('What is your Name', 'username');\n\t\t// Would produce:  <label for=\"username\">What is your Name</label>\n\n\tSimilar to other functions, you can submit an associative array in the\n\tthird parameter if you prefer to set additional attributes.\n\n\tExample::\n\n\t\t$attributes = array(\n\t\t\t'class' => 'mycustomclass',\n\t\t\t'style' => 'color: #000;'\n\t\t);\n\n\t\techo form_label('What is your Name', 'username', $attributes);\n\t\t// Would produce:  <label for=\"username\" class=\"mycustomclass\" style=\"color: #000;\">What is your Name</label>\n\n\n.. php:function:: form_submit([$data = ''[, $value = ''[, $extra = '']]])\n\n\t:param\tstring\t$data: Button name\n\t:param\tstring\t$value: Button value\n\t:param\tmixed\t$extra: Extra attributes to be added to the tag either as an array or a literal string\n\t:returns:\tAn HTML input submit tag\n\t:rtype:\tstring\n\n\tLets you generate a standard submit button. Simple example::\n\n\t\techo form_submit('mysubmit', 'Submit Post!');\n\t\t// Would produce:  <input type=\"submit\" name=\"mysubmit\" value=\"Submit Post!\" />\n\n\tSimilar to other functions, you can submit an associative array in the\n\tfirst parameter if you prefer to set your own attributes. The third\n\tparameter lets you add extra data to your form, like JavaScript.\n\n\n.. php:function:: form_reset([$data = ''[, $value = ''[, $extra = '']]])\n\n\t:param\tstring\t$data: Button name\n\t:param\tstring\t$value: Button value\n\t:param\tmixed\t$extra: Extra attributes to be added to the tag either as an array or a literal string\n\t:returns:\tAn HTML input reset button tag\n\t:rtype:\tstring\n\n\tLets you generate a standard reset button. Use is identical to\n\t:func:`form_submit()`.\n\n\n.. php:function:: form_button([$data = ''[, $content = ''[, $extra = '']]])\n\n\t:param\tstring\t$data: Button name\n\t:param\tstring\t$content: Button label\n\t:param\tmixed\t$extra: Extra attributes to be added to the tag either as an array or a literal string\n\t:returns:\tAn HTML button tag\n\t:rtype:\tstring\n\n\tLets you generate a standard button element. You can minimally pass the\n\tbutton name and content in the first and second parameter::\n\n\t\techo form_button('name','content');\n\t\t// Would produce: <button name=\"name\" type=\"button\">Content</button>\n\n\tOr you can pass an associative array containing any data you wish your\n\tform to contain::\n\n\t\t$data = array(\n\t\t\t'name'\t\t=> 'button',\n\t\t\t'id'\t\t=> 'button',\n\t\t\t'value'\t\t=> 'true',\n\t\t\t'type'\t\t=> 'reset',\n\t\t\t'content'\t=> 'Reset'\n\t\t);\n\n\t\techo form_button($data);\n\t\t// Would produce: <button name=\"button\" id=\"button\" value=\"true\" type=\"reset\">Reset</button>\n\n\tIf you would like your form to contain some additional data, like\n\tJavaScript, you can pass it as a string in the third parameter::\n\n\t\t$js = 'onClick=\"some_function()\"';\n\t\techo form_button('mybutton', 'Click Me', $js);\n\n\n.. php:function:: form_close([$extra = ''])\n\n\t:param\tstring\t$extra: Anything to append after the closing tag, *as is*\n\t:returns:\tAn HTML form closing tag\n\t:rtype:\tstring\n\n\tProduces a closing </form> tag. The only advantage to using this\n\tfunction is it permits you to pass data to it which will be added below\n\tthe tag. For example::\n\n\t\t$string = '</div></div>';\n\t\techo form_close($string);\n\t\t// Would produce:  </form> </div></div>\n\n\n.. php:function:: set_value($field[, $default = ''[, $html_escape = TRUE]])\n\n\t:param\tstring\t$field: Field name\n\t:param\tstring\t$default: Default value\n\t:param  bool\t$html_escape: Whether to turn off HTML escaping of the value\n\t:returns:\tField value\n\t:rtype:\tstring\n\n\tPermits you to set the value of an input form or textarea. You must\n\tsupply the field name via the first parameter of the function. The\n\tsecond (optional) parameter allows you to set a default value for the\n\tform. The third (optional) parameter allows you to turn off HTML escaping\n\tof the value, in case you need to use this function in combination with\n\ti.e. :php:func:`form_input()` and avoid double-escaping.\n\n\tExample::\n\n\t\t<input type=\"text\" name=\"quantity\" value=\"<?php echo set_value('quantity', '0'); ?>\" size=\"50\" />\n\n\tThe above form will show \"0\" when loaded for the first time.\n\n\t.. note:: If you've loaded the :doc:`Form Validation Library <../libraries/form_validation>` and\n\t\thave set a validation rule for the field name in use with this helper, then it will\n\t\tforward the call to the :doc:`Form Validation Library <../libraries/form_validation>`'s\n\t\town ``set_value()`` method. Otherwise, this function looks in ``$_POST`` for the\n\t\tfield value.\n\n.. php:function:: set_select($field[, $value = ''[, $default = FALSE]])\n\n\t:param\tstring\t$field: Field name\n\t:param\tstring\t$value: Value to check for\n\t:param\tstring\t$default: Whether the value is also a default one\n\t:returns:\t'selected' attribute or an empty string\n\t:rtype:\tstring\n\n\tIf you use a <select> menu, this function permits you to display the\n\tmenu item that was selected.\n\n\tThe first parameter must contain the name of the select menu, the second\n\tparameter must contain the value of each item, and the third (optional)\n\tparameter lets you set an item as the default (use boolean TRUE/FALSE).\n\n\tExample::\n\n\t\t<select name=\"myselect\">\n\t\t\t<option value=\"one\" <?php echo  set_select('myselect', 'one', TRUE); ?> >One</option>\n\t\t\t<option value=\"two\" <?php echo  set_select('myselect', 'two'); ?> >Two</option>\n\t\t\t<option value=\"three\" <?php echo  set_select('myselect', 'three'); ?> >Three</option>\n\t\t</select>\n\n.. php:function:: set_checkbox($field[, $value = ''[, $default = FALSE]])\n\n\t:param\tstring\t$field: Field name\n\t:param\tstring\t$value: Value to check for\n\t:param\tstring\t$default: Whether the value is also a default one\n\t:returns:\t'checked' attribute or an empty string\n\t:rtype:\tstring\n\n\tPermits you to display a checkbox in the state it was submitted.\n\n\tThe first parameter must contain the name of the checkbox, the second\n\tparameter must contain its value, and the third (optional) parameter\n\tlets you set an item as the default (use boolean TRUE/FALSE).\n\n\tExample::\n\n\t\t<input type=\"checkbox\" name=\"mycheck\" value=\"1\" <?php echo set_checkbox('mycheck', '1'); ?> />\n\t\t<input type=\"checkbox\" name=\"mycheck\" value=\"2\" <?php echo set_checkbox('mycheck', '2'); ?> />\n\n.. php:function:: set_radio($field[, $value = ''[, $default = FALSE]])\n\n\t:param\tstring\t$field: Field name\n\t:param\tstring\t$value: Value to check for\n\t:param\tstring\t$default: Whether the value is also a default one\n\t:returns:\t'checked' attribute or an empty string\n\t:rtype:\tstring\n\n\tPermits you to display radio buttons in the state they were submitted.\n\tThis function is identical to the :php:func:`set_checkbox()` function above.\n\n\tExample::\n\n\t\t<input type=\"radio\" name=\"myradio\" value=\"1\" <?php echo  set_radio('myradio', '1', TRUE); ?> />\n\t\t<input type=\"radio\" name=\"myradio\" value=\"2\" <?php echo  set_radio('myradio', '2'); ?> />\n\n\t.. note:: If you are using the Form Validation class, you must always specify\n\t\ta rule for your field, even if empty, in order for the ``set_*()``\n\t\tfunctions to work. This is because if a Form Validation object is\n\t\tdefined, the control for ``set_*()`` is handed over to a method of the\n\t\tclass instead of the generic helper function.\n\n.. php:function:: form_error([$field = ''[, $prefix = ''[, $suffix = '']]])\n\n\t:param\tstring\t$field:\tField name\n\t:param\tstring\t$prefix: Error opening tag\n\t:param\tstring\t$suffix: Error closing tag\n\t:returns:\tHTML-formatted form validation error message(s)\n\t:rtype:\tstring\n\n\tReturns a validation error message from the :doc:`Form Validation Library\n\t<../libraries/form_validation>`, associated with the specified field name.\n\tYou can optionally specify opening and closing tag(s) to put around the error\n\tmessage.\n\n\tExample::\n\n\t\t// Assuming that the 'username' field value was incorrect:\n\t\techo form_error('myfield', '<div class=\"error\">', '</div>');\n\n\t\t// Would produce: <div class=\"error\">Error message associated with the \"username\" field.</div>\n\n\n.. php:function:: validation_errors([$prefix = ''[, $suffix = '']])\n\n\t:param\tstring\t$prefix: Error opening tag\n\t:param\tstring\t$suffix: Error closing tag\n\t:returns:\tHTML-formatted form validation error message(s)\n\t:rtype:\tstring\n\n\tSimilarly to the :php:func:`form_error()` function, returns all validation\n\terror messages produced by the :doc:`Form Validation Library\n\t<../libraries/form_validation>`, with optional opening and closing tags\n\taround each of the messages.\n\n\tExample::\n\n\t\techo validation_errors('<span class=\"error\">', '</span>');\n\n\t\t/*\n\t\t\tWould produce, e.g.:\n\n\t\t\t<span class=\"error\">The \"email\" field doesn't contain a valid e-mail address!</span>\n\t\t\t<span class=\"error\">The \"password\" field doesn't match the \"repeat_password\" field!</span>\n\n\t\t */\n"
  },
  {
    "path": "user_guide_src/source/helpers/html_helper.rst",
    "content": "###########\nHTML Helper\n###########\n\nThe HTML Helper file contains functions that assist in working with\nHTML.\n\n.. contents::\n\t:local:\n\n.. raw:: html\n\n\t<div class=\"custom-index container\"></div>\n\nLoading this Helper\n===================\n\nThis helper is loaded using the following code::\n\n\t$this->load->helper('html');\n\nAvailable Functions\n===================\n\nThe following functions are available:\n\n\n.. php:function:: heading([$data = ''[, $h = '1'[, $attributes = '']]])\n\n\t:param\tstring\t$data: Content\n\t:param\tstring\t$h: Heading level\n\t:param\tmixed\t$attributes: HTML attributes\n\t:returns:\tHTML heading tag\n\t:rtype:\tstring\n\n\tLets you create HTML heading tags. The first parameter will contain the\n\tdata, the second the size of the heading. Example::\n\n\t\techo heading('Welcome!', 3);\n\n\tThe above would produce: <h3>Welcome!</h3>\n\n\tAdditionally, in order to add attributes to the heading tag such as HTML\n\tclasses, ids or inline styles, a third parameter accepts either a string\n\tor an array::\n\n\t\techo heading('Welcome!', 3, 'class=\"pink\"');\n\t\techo heading('How are you?', 4, array('id' => 'question', 'class' => 'green'));\n\n\tThe above code produces:\n\n\t.. code-block:: html\n\n\t\t<h3 class=\"pink\">Welcome!<h3>\n\t\t<h4 id=\"question\" class=\"green\">How are you?</h4>\n\n.. php:function:: img([$src = ''[, $index_page = FALSE[, $attributes = '']]])\n\n\t:param\tstring\t$src: Image source data\n\t:param\tbool\t$index_page: Whether to treat $src as a routed URI string\n\t:param\tarray\t$attributes: HTML attributes\n\t:returns:\tHTML image tag\n\t:rtype:\tstring\n\n\tLets you create HTML <img /> tags. The first parameter contains the\n\timage source. Example::\n\n\t\techo img('images/picture.jpg'); // gives <img src=\"http://site.com/images/picture.jpg\" />\n\n\tThere is an optional second parameter that is a TRUE/FALSE value that\n\tspecifics if the *src* should have the page specified by\n\t``$config['index_page']`` added to the address it creates.\n\tPresumably, this would be if you were using a media controller::\n\n\t\techo img('images/picture.jpg', TRUE); // gives <img src=\"http://site.com/index.php/images/picture.jpg\" alt=\"\" />\n\n\tAdditionally, an associative array can be passed to the ``img()`` function\n\tfor complete control over all attributes and values. If an *alt* attribute\n\tis not provided, CodeIgniter will generate an empty string.\n\n\tExample::\n\n\t\t$image_properties = array(\n\t\t\t'src' \t=> 'images/picture.jpg',\n\t\t\t'alt' \t=> 'Me, demonstrating how to eat 4 slices of pizza at one time',\n\t\t\t'class' => 'post_images',\n\t\t\t'width' => '200',\n\t\t\t'height'=> '200',\n\t\t\t'title' => 'That was quite a night',\n\t\t\t'rel' \t=> 'lightbox'\n\t\t);\n\n\t\timg($image_properties);\n\t\t// <img src=\"http://site.com/index.php/images/picture.jpg\" alt=\"Me, demonstrating how to eat 4 slices of pizza at one time\" class=\"post_images\" width=\"200\" height=\"200\" title=\"That was quite a night\" rel=\"lightbox\" />\n\n.. php:function:: link_tag([$href = ''[, $rel = 'stylesheet'[, $type = 'text/css'[, $title = ''[, $media = ''[, $index_page = FALSE]]]]]])\n\n\t:param\tstring\t$href: What are we linking to\n\t:param\tstring\t$rel: Relation type\n\t:param\tstring\t$type: Type of the related document\n\t:param\tstring\t$title: Link title\n\t:param\tstring\t$media: Media type\n\t:param\tbool\t$index_page: Whether to treat $src as a routed URI string\n\t:returns:\tHTML link tag\n\t:rtype:\tstring\n\n\tLets you create HTML <link /> tags. This is useful for stylesheet links,\n\tas well as other links. The parameters are *href*, with optional *rel*,\n\t*type*, *title*, *media* and *index_page*.\n\n\t*index_page* is a boolean value that specifies if the *href* should have\n\tthe page specified by ``$config['index_page']`` added to the address it creates.\n\n\tExample::\n\n\t\techo link_tag('css/mystyles.css');\n\t\t// gives <link href=\"http://site.com/css/mystyles.css\" rel=\"stylesheet\" type=\"text/css\" />\n\n\tFurther examples::\n\n\t\techo link_tag('favicon.ico', 'shortcut icon', 'image/ico');\n\t\t// <link href=\"http://site.com/favicon.ico\" rel=\"shortcut icon\" type=\"image/ico\" />\n\n\t\techo link_tag('feed', 'alternate', 'application/rss+xml', 'My RSS Feed');\n\t\t// <link href=\"http://site.com/feed\" rel=\"alternate\" type=\"application/rss+xml\" title=\"My RSS Feed\" />\n\n\tAdditionally, an associative array can be passed to the ``link()`` function\n\tfor complete control over all attributes and values::\n\n\t\t$link = array(\n\t\t\t'href'\t=> 'css/printer.css',\n\t\t\t'rel'\t=> 'stylesheet',\n\t\t\t'type'\t=> 'text/css',\n\t\t\t'media'\t=> 'print'\n\t\t);\n\n\t\techo link_tag($link);\n\t\t// <link href=\"http://site.com/css/printer.css\" rel=\"stylesheet\" type=\"text/css\" media=\"print\" />\n\n\n.. php:function:: ul($list[, $attributes = ''])\n\n\t:param\tarray\t$list: List entries\n\t:param\tarray\t$attributes: HTML attributes\n\t:returns:\tHTML-formatted unordered list\n\t:rtype:\tstring\n\n\tPermits you to generate unordered HTML lists from simple or\n\tmulti-dimensional arrays. Example::\n\n\t\t$list = array(\n\t\t\t'red',\n\t\t\t'blue',\n\t\t\t'green',\n\t\t\t'yellow'\n\t\t);\n\n\t\t$attributes = array(\n\t\t\t'class'\t=> 'boldlist',\n\t\t\t'id'\t=> 'mylist'\n\t\t);\n\n\t\techo ul($list, $attributes);\n\n\tThe above code will produce this:\n\n\t.. code-block:: html\n\n\t\t<ul class=\"boldlist\" id=\"mylist\">\n\t\t\t<li>red</li>\n\t\t\t<li>blue</li>\n\t\t\t<li>green</li>\n\t\t\t<li>yellow</li>\n\t\t</ul>\n\n\tHere is a more complex example, using a multi-dimensional array::\n\n\t\t$attributes = array(\n\t\t\t'class'\t=> 'boldlist',\n\t\t\t'id'\t=> 'mylist'\n\t\t);\n\n\t\t$list = array(\n\t\t\t'colors'  => array(\n\t\t\t\t'red',\n\t\t\t\t'blue',\n\t\t\t\t'green'\n\t\t\t),\n\t\t\t'shapes'  => array(\n\t\t\t\t'round',\n\t\t\t\t'square',\n\t\t\t\t'circles' => array(\n\t\t\t\t\t'ellipse',\n\t\t\t\t\t'oval',\n\t\t\t\t\t'sphere'\n\t\t\t\t)\n\t\t\t),\n\t\t\t'moods'  => array(\n\t\t\t\t'happy',\n\t\t\t\t'upset' => array(\n\t\t\t\t\t'defeated' => array(\n\t\t\t\t\t\t'dejected',\n\t\t\t\t\t\t'disheartened',\n\t\t\t\t\t\t'depressed'\n\t\t\t\t\t),\n\t\t\t\t\t'annoyed',\n\t\t\t\t\t'cross',\n\t\t\t\t\t'angry'\n\t\t\t\t)\n\t\t\t)\n\t\t);\n\n\t\techo ul($list, $attributes);\n\n\tThe above code will produce this:\n\n\t.. code-block:: html\n\n\t\t<ul class=\"boldlist\" id=\"mylist\">\n\t\t\t<li>colors\n\t\t\t\t<ul>\n\t\t\t\t\t<li>red</li>\n\t\t\t\t\t<li>blue</li>\n\t\t\t\t\t<li>green</li>\n\t\t\t\t</ul>\n\t\t\t</li>\n\t\t\t<li>shapes\n\t\t\t\t<ul>\n\t\t\t\t\t<li>round</li>\n\t\t\t\t\t<li>suare</li>\n\t\t\t\t\t<li>circles\n\t\t\t\t\t\t<ul>\n\t\t\t\t\t\t\t<li>elipse</li>\n\t\t\t\t\t\t\t<li>oval</li>\n\t\t\t\t\t\t\t<li>sphere</li>\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</li>\n\t\t\t<li>moods\n\t\t\t\t<ul>\n\t\t\t\t\t<li>happy</li>\n\t\t\t\t\t<li>upset\n\t\t\t\t\t\t<ul>\n\t\t\t\t\t\t\t<li>defeated\n\t\t\t\t\t\t\t\t<ul>\n\t\t\t\t\t\t\t\t\t<li>dejected</li>\n\t\t\t\t\t\t\t\t\t<li>disheartened</li>\n\t\t\t\t\t\t\t\t\t<li>depressed</li>\n\t\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t<li>annoyed</li>\n\t\t\t\t\t\t\t<li>cross</li>\n\t\t\t\t\t\t\t<li>angry</li>\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</li>\n\t\t</ul>\n\n.. php:function:: ol($list, $attributes = '')\n\n\t:param\tarray\t$list: List entries\n\t:param\tarray\t$attributes: HTML attributes\n\t:returns:\tHTML-formatted ordered list\n\t:rtype:\tstring\n\n\tIdentical to :php:func:`ul()`, only it produces the <ol> tag for\n\tordered lists instead of <ul>.\n\n.. php:function:: meta([$name = ''[, $content = ''[, $type = 'name'[, $newline = \"\\n\"]]]])\n\n\t:param\tstring\t$name: Meta name\n\t:param\tstring\t$content: Meta content\n\t:param\tstring\t$type: Meta type\n\t:param\tstring\t$newline: Newline character\n\t:returns:\tHTML meta tag\n\t:rtype:\tstring\n\n\tHelps you generate meta tags. You can pass strings to the function, or\n\tsimple arrays, or multidimensional ones.\n\n\tExamples::\n\n\t\techo meta('description', 'My Great site');\n\t\t// Generates:  <meta name=\"description\" content=\"My Great Site\" />\n\n\t\techo meta('Content-type', 'text/html; charset=utf-8', 'equiv');\n\t\t// Note the third parameter.  Can be \"charset\", \"http-equiv\", \"name\" or \"property\"\n\t\t// Generates:  <meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\" />\n\n\t\techo meta(array('name' => 'robots', 'content' => 'no-cache'));\n\t\t// Generates:  <meta name=\"robots\" content=\"no-cache\" />\n\n\t\t$meta = array(\n\t\t\tarray(\n\t\t\t\t'name' => 'robots',\n\t\t\t\t'content' => 'no-cache'\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'name' => 'description',\n\t\t\t\t'content' => 'My Great Site'\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'name' => 'keywords',\n\t\t\t\t'content' => 'love, passion, intrigue, deception'\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'name' => 'robots',\n\t\t\t\t'content' => 'no-cache'\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'name' => 'Content-Type',\n\t\t\t\t'type' => 'http-equiv',\n\t\t\t\t'content' => 'text/html; charset=utf-8'\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'name' => 'UTF-8',\n\t\t\t\t'type' => 'charset'\n\t\t\t)\n\t\t);\n\n\t\techo meta($meta);\n\t\t// Generates:\n\t\t// <meta name=\"robots\" content=\"no-cache\" />\n\t\t// <meta name=\"description\" content=\"My Great Site\" />\n\t\t// <meta name=\"keywords\" content=\"love, passion, intrigue, deception\" />\n\t\t// <meta name=\"robots\" content=\"no-cache\" />\n\t\t// <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n\t\t// <meta charset=\"UTF-8\" />\n\n\n.. php:function:: doctype([$type = 'html5'])\n\n\t:param\tstring\t$type: Doctype name\n\t:returns:\tHTML DocType tag\n\t:rtype:\tstring\n\n\tHelps you generate document type declarations, or DTD's. HTML 5\n\tis used by default, but many doctypes are available.\n\n\tExample::\n\n\t\techo doctype(); // <!DOCTYPE html>\n\n\t\techo doctype('html4-trans'); // <!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"https://www.w3.org/TR/html4/strict.dtd\">\n\n\tThe following is a list of doctype choices. These are configurable, and\n\tpulled from application/config/doctypes.php\n\n\t=============================== =================== ==================================================================================================================================================\n\tDocument type                   Option              Result\n\t=============================== =================== ==================================================================================================================================================\n\tXHTML 1.1                       xhtml11             <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"https://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n\tXHTML 1.0 Strict                xhtml1-strict       <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"https://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n\tXHTML 1.0 Transitional          xhtml1-trans        <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n\tXHTML 1.0 Frameset              xhtml1-frame        <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Frameset//EN\" \"https://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd\">\n\tXHTML Basic 1.1                 xhtml-basic11       <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML Basic 1.1//EN\" \"https://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd\">\n\tHTML 5                          html5               <!DOCTYPE html>\n\tHTML 4 Strict                   html4-strict        <!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"https://www.w3.org/TR/html4/strict.dtd\">\n\tHTML 4 Transitional             html4-trans         <!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"https://www.w3.org/TR/html4/loose.dtd\">\n\tHTML 4 Frameset                 html4-frame         <!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Frameset//EN\" \"https://www.w3.org/TR/html4/frameset.dtd\">\n\tMathML 1.01                     mathml1             <!DOCTYPE math SYSTEM \"https://www.w3.org/Math/DTD/mathml1/mathml.dtd\">\n\tMathML 2.0                      mathml2             <!DOCTYPE math PUBLIC \"-//W3C//DTD MathML 2.0//EN\" \"https://www.w3.org/Math/DTD/mathml2/mathml2.dtd\">\n\tSVG 1.0                         svg10               <!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"https://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n\tSVG 1.1 Full                    svg11               <!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"https://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n\tSVG 1.1 Basic                   svg11-basic         <!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1 Basic//EN\" \"https://www.w3.org/Graphics/SVG/1.1/DTD/svg11-basic.dtd\">\n\tSVG 1.1 Tiny                    svg11-tiny          <!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1 Tiny//EN\" \"https://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd\">\n\tXHTML+MathML+SVG (XHTML host)   xhtml-math-svg-xh   <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN\" \"https://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd\">\n\tXHTML+MathML+SVG (SVG host)     xhtml-math-svg-sh   <!DOCTYPE svg:svg PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN\" \"https://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd\">\n\tXHTML+RDFa 1.0                  xhtml-rdfa-1        <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML+RDFa 1.0//EN\" \"https://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd\">\n\tXHTML+RDFa 1.1                  xhtml-rdfa-2        <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML+RDFa 1.1//EN\" \"https://www.w3.org/MarkUp/DTD/xhtml-rdfa-2.dtd\">\n\t=============================== =================== ==================================================================================================================================================\n"
  },
  {
    "path": "user_guide_src/source/helpers/index.rst",
    "content": "#######\nHelpers\n#######\n\n.. toctree::\n\t:glob:\n\t:titlesonly:\n\t\n\t*"
  },
  {
    "path": "user_guide_src/source/helpers/inflector_helper.rst",
    "content": "################\nInflector Helper\n################\n\nThe Inflector Helper file contains functions that permits you to change\n**English** words to plural, singular, camel case, etc.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\nLoading this Helper\n===================\n\nThis helper is loaded using the following code::\n\n\t$this->load->helper('inflector');\n\nAvailable Functions\n===================\n\nThe following functions are available:\n\n\n.. php:function:: singular($str)\n\n\t:param\tstring\t$str: Input string\n\t:returns:\tA singular word\n\t:rtype:\tstring\n\n\tChanges a plural word to singular. Example::\n\n\t\techo singular('dogs'); // Prints 'dog'\n\n.. php:function:: plural($str)\n\n\t:param\tstring\t$str: Input string\n\t:returns:\tA plural word\n\t:rtype:\tstring\n\n\tChanges a singular word to plural. Example::\n\n\t\techo plural('dog'); // Prints 'dogs'\n\n.. php:function:: camelize($str)\n\n\t:param\tstring\t$str: Input string\n\t:returns:\tCamelized string\n\t:rtype:\tstring\n\n\tChanges a string of words separated by spaces or underscores to camel\n\tcase. Example::\n\n\t\techo camelize('my_dog_spot'); // Prints 'myDogSpot'\n\n.. php:function:: underscore($str)\n\n\t:param\tstring\t$str: Input string\n\t:returns:\tString containing underscores instead of spaces\n\t:rtype:\tstring\n\n\tTakes multiple words separated by spaces and underscores them.\n\tExample::\n\n\t\techo underscore('my dog spot'); // Prints 'my_dog_spot'\n\n.. php:function:: humanize($str[, $separator = '_'])\n\n\t:param\tstring\t$str: Input string\n\t:param\tstring\t$separator: Input separator\n\t:returns:\tHumanized string\n\t:rtype:\tstring\n\n\tTakes multiple words separated by underscores and adds spaces between\n\tthem. Each word is capitalized.\n\n\tExample::\n\n\t\techo humanize('my_dog_spot'); // Prints 'My Dog Spot'\n\n\tTo use dashes instead of underscores::\n\n\t\techo humanize('my-dog-spot', '-'); // Prints 'My Dog Spot'\n\n.. php:function:: word_is_countable($word)\n\n\t:param\tstring\t$word: Input string\n\t:returns:\tTRUE if the word is countable or FALSE if not\n\t:rtype:\tbool\n\n\tChecks if the given word has a plural version. Example::\n\n\t\tword_is_countable('equipment'); // Returns FALSE\n\n\t.. note:: This function used to be called ``is_countable()`` in\n\t\tin previous CodeIgniter versions.\n\n.. php:function:: ordinal_format($number)\n\n\t:param\tint\t$number: non-negative natural number to be converted\n\t:returns:\tOrdinal numeral for given number or original value on failure\n\t:rtype:\tstring\n\n\tReturns the ordinal numeral (1st, 2nd, 3rd etc.) for a\n\tnon-negative natural number. If the input is not a natural number\n\tgreater than 0, the function will return the original value. Examples::\n\n\t\techo ordinal_format(1); // Returns 1st\n\t\techo ordinal_format(3); // Returns 3rd\n\t\techo ordinal_format(21); // Returns 21st\n\t\techo ordinal_format(102); // Returns 102nd\n\t\techo ordinal_format(-5); // Invalid input, will return -5\n"
  },
  {
    "path": "user_guide_src/source/helpers/language_helper.rst",
    "content": "###############\nLanguage Helper\n###############\n\nThe Language Helper file contains functions that assist in working with\nlanguage files.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\nLoading this Helper\n===================\n\nThis helper is loaded using the following code::\n\n\t$this->load->helper('language');\n\nAvailable Functions\n===================\n\nThe following functions are available:\n\n\n.. php:function:: lang($line[, $for = ''[, $attributes = array()]])\n\n\t:param\tstring\t$line: Language line key\n\t:param\tstring\t$for: HTML \"for\" attribute (ID of the element we're creating a label for)\n\t:param\tarray\t$attributes: Any additional HTML attributes\n\t:returns:\tThe language line; in an HTML label tag, if the ``$for`` parameter is not empty\n\t:rtype:\tstring\n\n\tThis function returns a line of text from a loaded language file with\n\tsimplified syntax that may be more desirable for view files than\n\t``CI_Lang::line()``.\n\n\tExamples::\n\n\t\techo lang('language_key');\n\t\t// Outputs: Language line\n\n\t\techo lang('language_key', 'form_item_id', array('class' => 'myClass'));\n\t\t// Outputs: <label for=\"form_item_id\" class=\"myClass\">Language line</label>"
  },
  {
    "path": "user_guide_src/source/helpers/number_helper.rst",
    "content": "#############\nNumber Helper\n#############\n\nThe Number Helper file contains functions that help you work with\nnumeric data.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\nLoading this Helper\n===================\n\nThis helper is loaded using the following code::\n\n\t$this->load->helper('number');\n\nAvailable Functions\n===================\n\nThe following functions are available:\n\n\n.. php:function:: byte_format($num[, $precision = 1])\n\n\t:param\tmixed\t$num: Number of bytes\n\t:param\tint\t$precision: Floating point precision\n\t:returns:\tFormatted data size string\n\t:rtype:\tstring\n\n\tFormats numbers as bytes, based on size, and adds the appropriate\n\tsuffix. Examples::\n\n\t\techo byte_format(456); // Returns 456 Bytes\n\t\techo byte_format(4567); // Returns 4.5 KB\n\t\techo byte_format(45678); // Returns 44.6 KB\n\t\techo byte_format(456789); // Returns 447.8 KB\n\t\techo byte_format(3456789); // Returns 3.3 MB\n\t\techo byte_format(12345678912345); // Returns 1.8 GB\n\t\techo byte_format(123456789123456789); // Returns 11,228.3 TB\n\n\tAn optional second parameter allows you to set the precision of the\n\tresult::\n\n\t\t echo byte_format(45678, 2); // Returns 44.61 KB\n\n\t.. note:: The text generated by this function is found in the following\n\t\tlanguage file: *language/<your_lang>/number_lang.php*"
  },
  {
    "path": "user_guide_src/source/helpers/path_helper.rst",
    "content": "###########\nPath Helper\n###########\n\nThe Path Helper file contains functions that permits you to work with\nfile paths on the server.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\nLoading this Helper\n===================\n\nThis helper is loaded using the following code::\n\n\t$this->load->helper('path');\n\nAvailable Functions\n===================\n\nThe following functions are available:\n\n\n.. php:function:: set_realpath($path[, $check_existance = FALSE])\n\n\t:param\tstring\t$path: Path\n\t:param\tbool\t$check_existance: Whether to check if the path actually exists\n\t:returns:\tAn absolute path\n\t:rtype:\tstring\n\n\tThis function will return a server path without symbolic links or\n\trelative directory structures. An optional second argument will\n\tcause an error to be triggered if the path cannot be resolved.\n\n\tExamples::\n\n\t\t$file = '/etc/php5/apache2/php.ini';\n\t\techo set_realpath($file); // Prints '/etc/php5/apache2/php.ini'\n\n\t\t$non_existent_file = '/path/to/non-exist-file.txt';\n\t\techo set_realpath($non_existent_file, TRUE);\t// Shows an error, as the path cannot be resolved\n\t\techo set_realpath($non_existent_file, FALSE);\t// Prints '/path/to/non-exist-file.txt'\n\n\t\t$directory = '/etc/php5';\n\t\techo set_realpath($directory);\t// Prints '/etc/php5/'\n\n\t\t$non_existent_directory = '/path/to/nowhere';\n\t\techo set_realpath($non_existent_directory, TRUE);\t// Shows an error, as the path cannot be resolved\n\t\techo set_realpath($non_existent_directory, FALSE);\t// Prints '/path/to/nowhere'"
  },
  {
    "path": "user_guide_src/source/helpers/security_helper.rst",
    "content": "###############\nSecurity Helper\n###############\n\nThe Security Helper file contains security related functions.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\nLoading this Helper\n===================\n\nThis helper is loaded using the following code::\n\n\t$this->load->helper('security');\n\nAvailable Functions\n===================\n\nThe following functions are available:\n\n\n.. php:function:: xss_clean($str[, $is_image = FALSE])\n\n\t:param\tstring\t$str: Input data\n\t:param\tbool\t$is_image: Whether we're dealing with an image\n\t:returns:\tXSS-clean string\n\t:rtype:\tstring\n\n\tProvides Cross Site Script Hack filtering.\n\n\tThis function is an alias for ``CI_Input::xss_clean()``. For more info,\n\tplease see the :doc:`Input Library <../libraries/input>` documentation.\n\n.. php:function:: sanitize_filename($filename)\n\n\t:param\tstring\t$filename: Filename\n\t:returns:\tSanitized file name\n\t:rtype:\tstring\n\n\tProvides protection against directory traversal.\n\n\tThis function is an alias for ``CI_Security::sanitize_filename()``.\n\tFor more info, please see the :doc:`Security Library <../libraries/security>`\n\tdocumentation.\n\n.. php:function:: strip_image_tags($str)\n\n\t:param\tstring\t$str: Input string\n\t:returns:\tThe input string with no image tags\n\t:rtype:\tstring\n\n\tThis is a security function that will strip image tags from a string.\n\tIt leaves the image URL as plain text.\n\n\tExample::\n\n\t\t$string = strip_image_tags($string);\n\n\tThis function is an alias for ``CI_Security::strip_image_tags()``. For\n\tmore info, please see the :doc:`Security Library <../libraries/security>`\n\tdocumentation.\n\n\n.. php:function:: encode_php_tags($str)\n\n\t:param\tstring\t$str: Input string\n\t:returns:\tSafely formatted string\n\t:rtype:\tstring\n\n\tThis is a security function that converts PHP tags to entities.\n\n\t.. note:: :php:func:`xss_clean()` does this automatically, if you use it.\n\n\tExample::\n\n\t\t$string = encode_php_tags($string);\n"
  },
  {
    "path": "user_guide_src/source/helpers/string_helper.rst",
    "content": "#############\nString Helper\n#############\n\nThe String Helper file contains functions that assist in working with\nstrings.\n\n.. important:: Please note that these functions are NOT intended, nor\n\tsuitable to be used for any kind of security-related logic.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\nLoading this Helper\n===================\n\nThis helper is loaded using the following code::\n\n\t$this->load->helper('string');\n\nAvailable Functions\n===================\n\nThe following functions are available:\n\n.. php:function:: random_string([$type = 'alnum'[, $len = 8]])\n\n\t:param\tstring\t$type: Randomization type\n\t:param\tint\t$len: Output string length\n\t:returns:\tA random string\n\t:rtype:\tstring\n\n\tGenerates a random string based on the type and length you specify.\n\n\tThe first parameter specifies the type of string, the second parameter\n\tspecifies the length. The following choices are available:\n\n\t-  **alpha**: A string with lower and uppercase letters only.\n\t-  **alnum**: Alpha-numeric string with lower and uppercase characters.\n\t-  **basic**: A random number based on ``mt_rand()``.\n\t-  **numeric**: Numeric string.\n\t-  **nozero**: Numeric string with no zeros.\n\t-  **md5**: An encrypted random number based on ``md5()`` (fixed length of 32).\n\t-  **sha1**: An encrypted random number based on ``sha1()`` (fixed length of 40).\n\n\tUsage example::\n\n\t\techo random_string('alnum', 16);\n\n\t.. note:: Usage of this function is NOT suitable for password generation\n\t\tor other security-sensitive purposes. Please use\n\t\t`random_bytes() <https://secure.php.net/random_bytes>`_ instead.\n\n.. php:function:: increment_string($str[, $separator = '_'[, $first = 1]])\n\n\t:param\tstring\t$str: Input string\n\t:param\tstring\t$separator: Separator to append a duplicate number with\n\t:param\tint\t$first: Starting number\n\t:returns:\tAn incremented string\n\t:rtype:\tstring\n\n\tIncrements a string by appending a number to it or increasing the\n\tnumber. Useful for creating \"copies\" or a file or duplicating database\n\tcontent which has unique titles or slugs.\n\n\tUsage example::\n\n\t\techo increment_string('file', '_'); // \"file_1\"\n\t\techo increment_string('file', '-', 2); // \"file-2\"\n\t\techo increment_string('file_4'); // \"file_5\"\n\n\n.. php:function:: alternator($args)\n\n\t:param\tmixed\t$args: A variable number of arguments\n\t:returns:\tAlternated string(s)\n\t:rtype:\tmixed\n\n\tAllows two or more items to be alternated between, when cycling through\n\ta loop. Example::\n\n\t\tfor ($i = 0; $i < 10; $i++)\n\t\t{     \n\t\t\techo alternator('string one', 'string two');\n\t\t}\n\n\tYou can add as many parameters as you want, and with each iteration of\n\tyour loop the next item will be returned.\n\n\t::\n\n\t\tfor ($i = 0; $i < 10; $i++)\n\t\t{     \n\t\t\techo alternator('one', 'two', 'three', 'four', 'five');\n\t\t}\n\n\t.. note:: To use multiple separate calls to this function simply call the\n\t\tfunction with no arguments to re-initialize.\n\n.. php:function:: reduce_double_slashes($str)\n\n\t:param\tstring\t$str: Input string\n\t:returns:\tA string with normalized slashes\n\t:rtype:\tstring\n\n\tConverts double slashes in a string to a single slash, except those\n\tfound in URL protocol prefixes (e.g. \\http://).\n\n\tExample::\n\n\t\t$string = \"http://example.com//index.php\";\n\t\techo reduce_double_slashes($string); // results in \"http://example.com/index.php\"\n\n.. php:function:: strip_slashes($data)\n\n\t:param\tmixed\t$data: Input string or an array of strings\n\t:returns:\tString(s) with stripped slashes\n\t:rtype:\tmixed\n\n\tRemoves any slashes from an array of strings.\n\n\tExample::\n\n\t\t$str = array(\n\t\t\t'question'  => 'Is your name O\\'reilly?',\n\t\t\t'answer' => 'No, my name is O\\'connor.'\n\t\t);\n\n\t\t$str = strip_slashes($str);\n\n\tThe above will return the following array::\n\n\t\tarray(\n\t\t\t'question'  => \"Is your name O'reilly?\",\n\t\t\t'answer' => \"No, my name is O'connor.\"\n\t\t);\n\n\t.. note:: For historical reasons, this function will also accept\n\t\tand handle string inputs. This however makes it just an\n\t\talias for ``stripslashes()``.\n\n.. php:function:: reduce_multiples($str[, $character = ''[, $trim = FALSE]])\n\n\t:param\tstring\t$str: Text to search in\n\t:param\tstring\t$character: Character to reduce\n\t:param\tbool\t$trim: Whether to also trim the specified character\n\t:returns:\tReduced string\n\t:rtype:\tstring\n\n\tReduces multiple instances of a particular character occurring directly\n\tafter each other. Example::\n\n\t\t$string = \"Fred, Bill,, Joe, Jimmy\";\n\t\t$string = reduce_multiples($string,\",\"); //results in \"Fred, Bill, Joe, Jimmy\"\n\n\tIf the third parameter is set to TRUE it will remove occurrences of the\n\tcharacter at the beginning and the end of the string. Example::\n\n\t\t$string = \",Fred, Bill,, Joe, Jimmy,\";\n\t\t$string = reduce_multiples($string, \", \", TRUE); //results in \"Fred, Bill, Joe, Jimmy\"\n\n.. php:function:: quotes_to_entities($str)\n\n\t:param\tstring\t$str: Input string\n\t:returns:\tString with quotes converted to HTML entities\n\t:rtype:\tstring\n\n\tConverts single and double quotes in a string to the corresponding HTML\n\tentities. Example::\n\n\t\t$string = \"Joe's \\\"dinner\\\"\";\n\t\t$string = quotes_to_entities($string); //results in \"Joe&#39;s &quot;dinner&quot;\"\n\n\n.. php:function:: strip_quotes($str)\n\n\t:param\tstring\t$str: Input string\n\t:returns:\tString with quotes stripped\n\t:rtype:\tstring\n\n\tRemoves single and double quotes from a string. Example::\n\n\t\t$string = \"Joe's \\\"dinner\\\"\";\n\t\t$string = strip_quotes($string); //results in \"Joes dinner\"\n"
  },
  {
    "path": "user_guide_src/source/helpers/text_helper.rst",
    "content": "###########\nText Helper\n###########\n\nThe Text Helper file contains functions that assist in working with\ntext.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\nLoading this Helper\n===================\n\nThis helper is loaded using the following code::\n\n\t$this->load->helper('text');\n\nAvailable Functions\n===================\n\nThe following functions are available:\n\n.. php:function:: word_limiter($str[, $limit = 100[, $end_char = '&#8230;']])\n\n\t:param\tstring\t$str: Input string\n\t:param\tint\t$limit: Limit\n\t:param\tstring\t$end_char: End character (usually an ellipsis)\n\t:returns:\tWord-limited string\n\t:rtype:\tstring\n\n\tTruncates a string to the number of *words* specified. Example::\n\n\t\t$string = \"Here is a nice text string consisting of eleven words.\";\n\t\t$string = word_limiter($string, 4);\n\t\t// Returns:  Here is a nice\n\n\tThe third parameter is an optional suffix added to the string. By\n\tdefault it adds an ellipsis.\n\n\n.. php:function:: character_limiter($str[, $n = 500[, $end_char = '&#8230;']])\n\n\t:param\tstring\t$str: Input string\n\t:param\tint\t$n: Number of characters\n\t:param\tstring\t$end_char: End character (usually an ellipsis)\n\t:returns:\tCharacter-limited string\n\t:rtype:\tstring\n\n\tTruncates a string to the number of *characters* specified. It\n\tmaintains the integrity of words so the character count may be slightly\n\tmore or less than what you specify.\n\n\tExample::\n\n\t\t$string = \"Here is a nice text string consisting of eleven words.\";\n\t\t$string = character_limiter($string, 20);\n\t\t// Returns:  Here is a nice text string\n\n\tThe third parameter is an optional suffix added to the string, if\n\tundeclared this helper uses an ellipsis.\n\n\t.. note:: If you need to truncate to an exact number of characters please\n\t\tsee the :php:func:`ellipsize()` function below.\n\n.. php:function:: ascii_to_entities($str)\n\n\t:param\tstring\t$str: Input string\n\t:returns:\tA string with ASCII values converted to entities\n\t:rtype:\tstring\n\n\tConverts ASCII values to character entities, including high ASCII and MS\n\tWord characters that can cause problems when used in a web page, so that\n\tthey can be shown consistently regardless of browser settings or stored\n\treliably in a database. There is some dependence on your server's\n\tsupported character sets, so it may not be 100% reliable in all cases,\n\tbut for the most part it should correctly identify characters outside\n\tthe normal range (like accented characters).\n\n\tExample::\n\n\t\t$string = ascii_to_entities($string);\n\n.. php:function::entities_to_ascii($str[, $all = TRUE])\n\n\t:param\tstring\t$str: Input string\n\t:param\tbool\t$all: Whether to convert unsafe entities as well\n\t:returns:\tA string with HTML entities converted to ASCII characters\n\t:rtype:\tstring\n\n\tThis function does the opposite of :php:func:`ascii_to_entities()`.\n\tIt turns character entities back into ASCII.\n\n.. php:function:: convert_accented_characters($str)\n\n\t:param\tstring\t$str: Input string\n\t:returns:\tA string with accented characters converted\n\t:rtype:\tstring\n\n\tTransliterates high ASCII characters to low ASCII equivalents. Useful\n\twhen non-English characters need to be used where only standard ASCII\n\tcharacters are safely used, for instance, in URLs.\n\n\tExample::\n\n\t\t$string = convert_accented_characters($string);\n\n\t.. note:: This function uses a companion config file\n\t\t`application/config/foreign_chars.php` to define the to and\n\t\tfrom array for transliteration.\n\n.. php:function:: word_censor($str, $censored[, $replacement = ''])\n\n\t:param\tstring\t$str: Input string\n\t:param\tarray\t$censored: List of bad words to censor\n\t:param\tstring\t$replacement: What to replace bad words with\n\t:returns:\tCensored string\n\t:rtype:\tstring\n\n\tEnables you to censor words within a text string. The first parameter\n\twill contain the original string. The second will contain an array of\n\twords which you disallow. The third (optional) parameter can contain\n\ta replacement value for the words. If not specified they are replaced\n\twith pound signs: ####.\n\n\tExample::\n\n\t\t$disallowed = array('darn', 'shucks', 'golly', 'phooey');\n\t\t$string = word_censor($string, $disallowed, 'Beep!');\n\n.. php:function:: highlight_code($str)\n\n\t:param\tstring\t$str: Input string\n\t:returns:\tString with code highlighted via HTML\n\t:rtype:\tstring\n\n\tColorizes a string of code (PHP, HTML, etc.). Example::\n\n\t\t$string = highlight_code($string);\n\n\tThe function uses PHP's ``highlight_string()`` function, so the\n\tcolors used are the ones specified in your php.ini file.\n\n\n.. php:function:: highlight_phrase($str, $phrase[, $tag_open = '<mark>'[, $tag_close = '</mark>']])\n\n\t:param\tstring\t$str: Input string\n\t:param\tstring\t$phrase: Phrase to highlight\n\t:param\tstring\t$tag_open: Opening tag used for the highlight\n\t:param\tstring\t$tag_close: Closing tag for the highlight\n\t:returns:\tString with a phrase highlighted via HTML\n\t:rtype:\tstring\n\n\tWill highlight a phrase within a text string. The first parameter will\n\tcontain the original string, the second will contain the phrase you wish\n\tto highlight. The third and fourth parameters will contain the\n\topening/closing HTML tags you would like the phrase wrapped in.\n\n\tExample::\n\n\t\t$string = \"Here is a nice text string about nothing in particular.\";\n\t\techo highlight_phrase($string, \"nice text\", '<span style=\"color:#990000;\">', '</span>');\n\n\tThe above code prints::\n\n\t\tHere is a <span style=\"color:#990000;\">nice text</span> string about nothing in particular.\n\n\t.. note:: This function used to use the ``<strong>`` tag by default. Older browsers\n\t\tmight not support the new HTML5 mark tag, so it is recommended that you\n\t\tinsert the following CSS code into your stylesheet if you need to support\n\t\tsuch browsers::\n\n\t\t\tmark {\n\t\t\t\tbackground: #ff0;\n\t\t\t\tcolor: #000;\n\t\t\t};\n\n.. php:function:: word_wrap($str[, $charlim = 76])\n\n\t:param\tstring\t$str: Input string\n\t:param\tint\t$charlim: Character limit\n\t:returns:\tWord-wrapped string\n\t:rtype:\tstring\n\n\tWraps text at the specified *character* count while maintaining\n\tcomplete words.\n\n\tExample::\n\n\t\t$string = \"Here is a simple string of text that will help us demonstrate this function.\";\n\t\techo word_wrap($string, 25);\n\n\t\t// Would produce:  \n\t\t// Here is a simple string\n\t\t// of text that will help us\n\t\t// demonstrate this\n\t\t// function.\n\n.. php:function:: ellipsize($str, $max_length[, $position = 1[, $ellipsis = '&hellip;']])\n\n\t:param\tstring\t$str: Input string\n\t:param\tint\t$max_length: String length limit\n\t:param\tmixed\t$position: Position to split at (int or float)\n\t:param\tstring\t$ellipsis: What to use as the ellipsis character\n\t:returns:\tEllipsized string\n\t:rtype:\tstring\n\n\tThis function will strip tags from a string, split it at a defined\n\tmaximum length, and insert an ellipsis.\n\n\tThe first parameter is the string to ellipsize, the second is the number\n\tof characters in the final string. The third parameter is where in the\n\tstring the ellipsis should appear from 0 - 1, left to right. For\n\texample. a value of 1 will place the ellipsis at the right of the\n\tstring, .5 in the middle, and 0 at the left.\n\n\tAn optional forth parameter is the kind of ellipsis. By default,\n\t&hellip; will be inserted.\n\n\tExample::\n\n\t\t$str = 'this_string_is_entirely_too_long_and_might_break_my_design.jpg';\n\t\techo ellipsize($str, 32, .5);\n\n\tProduces::\n\n\t\tthis_string_is_e&hellip;ak_my_design.jpg"
  },
  {
    "path": "user_guide_src/source/helpers/typography_helper.rst",
    "content": "#################\nTypography Helper\n#################\n\nThe Typography Helper file contains functions that help your format text\nin semantically relevant ways.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\nLoading this Helper\n===================\n\nThis helper is loaded using the following code::\n\n\t$this->load->helper('typography');\n\nAvailable Functions\n===================\n\nThe following functions are available:\n\n\n.. php:function:: auto_typography($str[, $reduce_linebreaks = FALSE])\n\n\t:param\tstring\t$str: Input string\n\t:param\tbool\t$reduce_linebreaks: Whether to reduce multiple instances of double newlines to two\n\t:returns:\tHTML-formatted typography-safe string\n\t:rtype: string\n\n\tFormats text so that it is semantically and typographically correct\n\tHTML.\n\n\tThis function is an alias for ``CI_Typography::auto_typography()``.\n\tFor more info, please see the :doc:`Typography Library\n\t<../libraries/typography>` documentation.\n\n\tUsage example::\n\n\t\t$string = auto_typography($string);\n\n\t.. note:: Typographic formatting can be processor intensive, particularly if\n\t\tyou have a lot of content being formatted. If you choose to use this\n\t\tfunction you may want to consider :doc:`caching <../general/caching>` your\n\t\tpages.\n\n\n.. php:function:: nl2br_except_pre($str)\n\n\t:param\tstring\t$str: Input string\n\t:returns:\tString with HTML-formatted line breaks\n\t:rtype:\tstring\n\n\tConverts newlines to <br /> tags unless they appear within <pre> tags.\n\tThis function is identical to the native PHP ``nl2br()`` function,\n\texcept that it ignores <pre> tags.\n\n\tUsage example::\n\n\t\t$string = nl2br_except_pre($string);\n\n.. php:function:: entity_decode($str, $charset = NULL)\n\n\t:param\tstring\t$str: Input string\n\t:param\tstring\t$charset: Character set\n\t:returns:\tString with decoded HTML entities\n\t:rtype:\tstring\n\n\tThis function is an alias for ``CI_Security::entity_decode()``.\n\tFore more info, please see the :doc:`Security Library\n\t<../libraries/security>` documentation."
  },
  {
    "path": "user_guide_src/source/helpers/url_helper.rst",
    "content": "##########\nURL Helper\n##########\n\nThe URL Helper file contains functions that assist in working with URLs.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\nLoading this Helper\n===================\n\nThis helper is loaded using the following code::\n\n\t$this->load->helper('url');\n\nAvailable Functions\n===================\n\nThe following functions are available:\n\n.. php:function:: site_url([$uri = ''[, $protocol = NULL]])\n\n\t:param\tstring\t$uri: URI string\n\t:param\tstring\t$protocol: Protocol, e.g. 'http' or 'https'\n\t:returns:\tSite URL\n\t:rtype:\tstring\n\n\tReturns your site URL, as specified in your config file. The index.php\n\tfile (or whatever you have set as your site **index_page** in your config\n\tfile) will be added to the URL, as will any URI segments you pass to the\n\tfunction, plus the **url_suffix** as set in your config file.\n\n\tYou are encouraged to use this function any time you need to generate a\n\tlocal URL so that your pages become more portable in the event your URL\n\tchanges.\n\n\tSegments can be optionally passed to the function as a string or an\n\tarray. Here is a string example::\n\n\t\techo site_url('news/local/123');\n\n\tThe above example would return something like:\n\t*http://example.com/index.php/news/local/123*\n\n\tHere is an example of segments passed as an array::\n\n\t\t$segments = array('news', 'local', '123');\n\t\techo site_url($segments);\n\n\tThis function is an alias for ``CI_Config::site_url()``. For more info,\n\tplease see the :doc:`Config Library <../libraries/config>` documentation.\n\n.. php:function:: base_url($uri = '', $protocol = NULL)\n\n\t:param\tstring\t$uri: URI string\n\t:param\tstring\t$protocol: Protocol, e.g. 'http' or 'https'\n\t:returns:\tBase URL\n\t:rtype:\tstring\n\n\tReturns your site base URL, as specified in your config file. Example::\n\n\t\techo base_url();\n\n\tThis function returns the same thing as :php:func:`site_url()`, without\n\tthe *index_page* or *url_suffix* being appended.\n\n\tAlso like :php:func:`site_url()`, you can supply segments as a string or\n\tan array. Here is a string example::\n\n\t\techo base_url(\"blog/post/123\");\n\n\tThe above example would return something like:\n\t*http://example.com/blog/post/123*\n\n\tThis is useful because unlike :php:func:`site_url()`, you can supply a\n\tstring to a file, such as an image or stylesheet. For example::\n\n\t\techo base_url(\"images/icons/edit.png\");\n\n\tThis would give you something like:\n\t*http://example.com/images/icons/edit.png*\n\n\tThis function is an alias for ``CI_Config::base_url()``. For more info,\n\tplease see the :doc:`Config Library <../libraries/config>` documentation.\n\n.. php:function:: current_url()\n\n\t:returns:\tThe current URL\n\t:rtype:\tstring\n\n\tReturns the full URL (including segments) of the page being currently\n\tviewed.\n\n\t.. note:: Calling this function is the same as doing this:\n\t\t|\n\t\t| site_url(uri_string());\n\n\n.. php:function:: uri_string()\n\n\t:returns:\tAn URI string\n\t:rtype:\tstring\n\n\tReturns the URI segments of any page that contains this function.\n\tFor example, if your URL was this::\n\n\t\thttp://some-site.com/blog/comments/123\n\n\tThe function would return::\n\n\t\tblog/comments/123\n\n\tThis function is an alias for ``CI_Config::uri_string()``. For more info,\n\tplease see the :doc:`Config Library <../libraries/config>` documentation.\n\n\n.. php:function:: index_page()\n\n\t:returns:\t'index_page' value\n\t:rtype:\tmixed\n\n\tReturns your site **index_page**, as specified in your config file.\n\tExample::\n\n\t\techo index_page();\n\n.. php:function:: anchor($uri = '', $title = '', $attributes = '')\n\n\t:param\tstring\t$uri: URI string\n\t:param\tstring\t$title: Anchor title\n\t:param\tmixed\t$attributes: HTML attributes\n\t:returns:\tHTML hyperlink (anchor tag)\n\t:rtype:\tstring\n\n\tCreates a standard HTML anchor link based on your local site URL.\n\n\tThe first parameter can contain any segments you wish appended to the\n\tURL. As with the :php:func:`site_url()` function above, segments can\n\tbe a string or an array.\n\n\t.. note:: If you are building links that are internal to your application\n\t\tdo not include the base URL (\\http://...). This will be added\n\t\tautomatically from the information specified in your config file.\n\t\tInclude only the URI segments you wish appended to the URL.\n\n\tThe second segment is the text you would like the link to say. If you\n\tleave it blank, the URL will be used.\n\n\tThe third parameter can contain a list of attributes you would like\n\tadded to the link. The attributes can be a simple string or an\n\tassociative array.\n\n\tHere are some examples::\n\n\t\techo anchor('news/local/123', 'My News', 'title=\"News title\"');\n\t\t// Prints: <a href=\"http://example.com/index.php/news/local/123\" title=\"News title\">My News</a>\n\n\t\techo anchor('news/local/123', 'My News', array('title' => 'The best news!'));\n\t\t// Prints: <a href=\"http://example.com/index.php/news/local/123\" title=\"The best news!\">My News</a>\n\n\t\techo anchor('', 'Click here');\n\t\t// Prints: <a href=\"http://example.com\">Click Here</a>\n\n\n.. php:function:: anchor_popup($uri = '', $title = '', $attributes = FALSE)\n\n\t:param\tstring\t$uri: URI string\n\t:param\tstring\t$title: Anchor title\n\t:param\tmixed\t$attributes: HTML attributes\n\t:returns:\tPop-up hyperlink\n\t:rtype:\tstring\n\n\tNearly identical to the :php:func:`anchor()` function except that it\n\topens the URL in a new window. You can specify JavaScript window\n\tattributes in the third parameter to control how the window is opened.\n\tIf the third parameter is not set it will simply open a new window with\n\tyour own browser settings.\n\n\tHere is an example with attributes::\n\n\t\t$atts = array(\n\t\t\t'width'       => 800,\n\t\t\t'height'      => 600,\n\t\t\t'scrollbars'  => 'yes',\n\t\t\t'status'      => 'yes',\n\t\t\t'resizable'   => 'yes',\n\t\t\t'screenx'     => 0,\n\t\t\t'screeny'     => 0,\n\t\t\t'window_name' => '_blank'\n\t\t);\n\n\t\techo anchor_popup('news/local/123', 'Click Me!', $atts);\n\n\t.. note:: The above attributes are the function defaults so you only need to\n\t\tset the ones that are different from what you need. If you want the\n\t\tfunction to use all of its defaults simply pass an empty array in the\n\t\tthird parameter:\n\t\t|\n\t\t| echo anchor_popup('news/local/123', 'Click Me!', array());\n\n\t.. note:: The **window_name** is not really an attribute, but an argument to\n\t\tthe JavaScript `window.open() <https://www.w3schools.com/jsref/met_win_open.asp>`\n\t\tmethod, which accepts either a window name or a window target.\n\n\t.. note:: Any other attribute than the listed above will be parsed as an\n\t\tHTML attribute to the anchor tag.\n\n\n.. php:function:: mailto($email, $title = '', $attributes = '')\n\n\t:param\tstring\t$email: E-mail address\n\t:param\tstring\t$title: Anchor title\n\t:param\tmixed\t$attributes: HTML attributes\n\t:returns:\tA \"mail to\" hyperlink\n\t:rtype:\tstring\n\n\tCreates a standard HTML e-mail link. Usage example::\n\n\t\techo mailto('me@my-site.com', 'Click Here to Contact Me');\n\n\tAs with the :php:func:`anchor()` tab above, you can set attributes using the\n\tthird parameter::\n\n\t\t$attributes = array('title' => 'Mail me');\n\t\techo mailto('me@my-site.com', 'Contact Me', $attributes);\n\n.. php:function:: safe_mailto($email, $title = '', $attributes = '')\n\n\t:param\tstring\t$email: E-mail address\n\t:param\tstring\t$title: Anchor title\n\t:param\tmixed\t$attributes: HTML attributes\n\t:returns:\tA spam-safe \"mail to\" hyperlink\n\t:rtype:\tstring\n\n\tIdentical to the :php:func:`mailto()` function except it writes an obfuscated\n\tversion of the *mailto* tag using ordinal numbers written with JavaScript to\n\thelp prevent the e-mail address from being harvested by spam bots.\n\n.. php:function:: auto_link($str, $type = 'both', $popup = FALSE)\n\n\t:param\tstring\t$str: Input string\n\t:param\tstring\t$type: Link type ('email', 'url' or 'both')\n\t:param\tbool\t$popup: Whether to create popup links\n\t:returns:\tLinkified string\n\t:rtype:\tstring\n\n\tAutomatically turns URLs and e-mail addresses contained in a string into\n\tlinks. Example::\n\n\t\t$string = auto_link($string);\n\n\tThe second parameter determines whether URLs and e-mails are converted or\n\tjust one or the other. Default behavior is both if the parameter is not\n\tspecified. E-mail links are encoded as :php:func:`safe_mailto()` as shown\n\tabove.\n\n\tConverts only URLs::\n\n\t\t$string = auto_link($string, 'url');\n\n\tConverts only e-mail addresses::\n\n\t\t$string = auto_link($string, 'email');\n\n\tThe third parameter determines whether links are shown in a new window.\n\tThe value can be TRUE or FALSE (boolean)::\n\n\t\t$string = auto_link($string, 'both', TRUE);\n\n\n.. php:function:: url_title($str, $separator = '-', $lowercase = FALSE)\n\n\t:param\tstring\t$str: Input string\n\t:param\tstring\t$separator: Word separator\n\t:param\tbool\t$lowercase: Whether to transform the output string to lower-case\n\t:returns:\tURL-formatted string\n\t:rtype:\tstring\n\n\tTakes a string as input and creates a human-friendly URL string. This is\n\tuseful if, for example, you have a blog in which you'd like to use the\n\ttitle of your entries in the URL. Example::\n\n\t\t$title = \"What's wrong with CSS?\";\n\t\t$url_title = url_title($title);\n\t\t// Produces: Whats-wrong-with-CSS\n\n\tThe second parameter determines the word delimiter. By default dashes\n\tare used. Preferred options are: **-** (dash) or **_** (underscore)\n\n\tExample::\n\n\t\t$title = \"What's wrong with CSS?\";\n\t\t$url_title = url_title($title, 'underscore');\n\t\t// Produces: Whats_wrong_with_CSS\n\n\tThe third parameter determines whether or not lowercase characters are\n\tforced. By default they are not. Options are boolean TRUE/FALSE.\n\n\tExample::\n\n\t\t$title = \"What's wrong with CSS?\";\n\t\t$url_title = url_title($title, 'underscore', TRUE);\n\t\t// Produces: whats_wrong_with_css\n\n\n.. php:function:: prep_url($str = '')\n\n\t:param\tstring\t$str: URL string\n\t:returns:\tProtocol-prefixed URL string\n\t:rtype:\tstring\n\n\tThis function will add \\http:// in the event that a protocol prefix\n\tis missing from a URL.\n\n\tPass the URL string to the function like this::\n\n\t\t$url = prep_url('example.com');\n\n\n.. php:function:: redirect($uri = '', $method = 'auto', $code = NULL)\n\n\t:param\tstring\t$uri: URI string\n\t:param\tstring\t$method: Redirect method ('auto', 'location' or 'refresh')\n\t:param\tstring\t$code: HTTP Response code (usually 302 or 303)\n\t:rtype:\tvoid\n\n\tDoes a \"header redirect\" to the URI specified. If you specify the full\n\tsite URL that link will be built, but for local links simply providing\n\tthe URI segments to the controller you want to direct to will create the\n\tlink. The function will build the URL based on your config file values.\n\n\tThe optional second parameter allows you to force a particular redirection\n\tmethod. The available methods are **auto**, **location** and **refresh**,\n\twith location being faster but less reliable on IIS servers.\n\tThe default is **auto**, which will attempt to intelligently choose the\n\tmethod based on the server environment.\n\n\tThe optional third parameter allows you to send a specific HTTP Response\n\tCode - this could be used for example to create 301 redirects for search\n\tengine purposes. The default Response Code is 302. The third parameter is\n\t*only* available with **location** redirects, and not *refresh*. Examples::\n\n\t\tif ($logged_in == FALSE)\n\t\t{      \n\t\t\tredirect('/login/form/');\n\t\t}\n\n\t\t// with 301 redirect\n\t\tredirect('/article/13', 'location', 301);\n\n\t.. note:: In order for this function to work it must be used before anything\n\t\tis outputted to the browser since it utilizes server headers.\n\n\t.. note:: For very fine grained control over headers, you should use the\n\t\t:doc:`Output Library </libraries/output>` ``set_header()`` method.\n\n\t.. note:: To IIS users: if you hide the `Server` HTTP header, the *auto*\n\t\tmethod won't detect IIS, in that case it is advised you explicitly\n\t\tuse the **refresh** method.\n\n\t.. note:: When the **location** method is used, an HTTP status code of 303\n\t\twill *automatically* be selected when the page is currently accessed\n\t\tvia POST and HTTP/1.1 is used.\n\n\t.. important:: This function will terminate script execution.\n"
  },
  {
    "path": "user_guide_src/source/helpers/xml_helper.rst",
    "content": "##########\nXML Helper\n##########\n\nThe XML Helper file contains functions that assist in working with XML\ndata.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\nLoading this Helper\n===================\n\nThis helper is loaded using the following code\n\n::\n\n\t$this->load->helper('xml');\n\nAvailable Functions\n===================\n\nThe following functions are available:\n\n.. php:function:: xml_convert($str[, $protect_all = FALSE])\n\n\t:param string $str: the text string to convert\n\t:param bool $protect_all: Whether to protect all content that looks like a potential entity instead of just numbered entities, e.g. &foo;\n\t:returns: XML-converted string\n\t:rtype:\tstring\n\n\tTakes a string as input and converts the following reserved XML\n\tcharacters to entities:\n\n\t  - Ampersands: &\n\t  - Less than and greater than characters: < >\n\t  - Single and double quotes: ' \"\n\t  - Dashes: -\n\n\tThis function ignores ampersands if they are part of existing numbered\n\tcharacter entities, e.g. &#123;. Example::\n\n\t\t$string = '<p>Here is a paragraph & an entity (&#123;).</p>';\n\t\t$string = xml_convert($string);\n\t\techo $string;\n\n\toutputs:\n\n\t.. code-block:: html\n\n\t\t&lt;p&gt;Here is a paragraph &amp; an entity (&#123;).&lt;/p&gt;"
  },
  {
    "path": "user_guide_src/source/index.rst",
    "content": "########################\nCodeIgniter 3 User Guide\n########################\n\n- :doc:`License Agreement <license>`\n- :doc:`Change Log <changelog>`\n\n.. contents::\n   :local:\n   :depth: 2\n\n*************\nCodeIgniter 3\n*************\n\nCodeIgniter 3 is the legacy version of the framework, intended for use with PHP\n5.6+. This version is in maintenance, receiving mostly just security updates.\n\n`CodeIgniter 4 <https://codeigniter.com/user_guide/>`_ is the latest version of\nthe framework.\n\n*******\nWelcome\n*******\n\n.. toctree::\n\t:titlesonly:\n\n\tgeneral/welcome\n\n**********\nBasic Info\n**********\n\n- :doc:`general/requirements`\n- :doc:`general/credits`\n\n************\nInstallation\n************\n.. toctree::\n\t:includehidden:\n\t:maxdepth: 2\n\t:titlesonly:\n\n\tinstallation/index\n\n************\nIntroduction\n************\n\n.. toctree::\n\t:titlesonly:\n\n\toverview/index\n\n********\nTutorial\n********\n\n.. toctree::\n\t:includehidden:\n\t:titlesonly:\n\n\ttutorial/index\n\n***************************\nContributing to CodeIgniter\n***************************\n\n.. toctree::\n\t:glob:\n\t:titlesonly:\n\n\tcontributing/index\n\n**************\nGeneral Topics\n**************\n\n.. toctree::\n\t:glob:\n\t:titlesonly:\n\n\tgeneral/index\n\n*****************\nLibrary Reference\n*****************\n\n.. toctree::\n\t:glob:\n\t:titlesonly:\n\n\tlibraries/index\n\n******************\nDatabase Reference\n******************\n\n.. toctree::\n\t:glob:\n\t:titlesonly:\n\n\tdatabase/index\n\n****************\nHelper Reference\n****************\n\n.. toctree::\n\t:glob:\n\t:titlesonly:\n\n\thelpers/index\n\n.. toctree::\n\t:glob:\n\t:titlesonly:\n\t:hidden:\n\n\t*\n\toverview/index\n\tgeneral/requirements\n\tgeneral/welcome\n\tinstallation/index\n\tgeneral/index\n\tlibraries/index\n\tdatabase/index\n\thelpers/index\n\ttutorial/index\n\tgeneral/credits\n"
  },
  {
    "path": "user_guide_src/source/installation/downloads.rst",
    "content": "#######################\nDownloading CodeIgniter\n#######################\n\n-  `CodeIgniter v3.2.0-dev (Current version) <https://codeload.github.com/bcit-ci/CodeIgniter/zip/develop>`_\n-  `CodeIgniter v3.1.14 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.1.14>`_\n-  `CodeIgniter v3.1.13 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.1.13>`_\n-  `CodeIgniter v3.1.12 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.1.12>`_\n-  `CodeIgniter v3.1.11 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.1.11>`_\n-  `CodeIgniter v3.1.10 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.1.10>`_\n-  `CodeIgniter v3.1.9 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.1.9>`_\n-  `CodeIgniter v3.1.8 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.1.8>`_\n-  `CodeIgniter v3.1.7 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.1.7>`_\n-  `CodeIgniter v3.1.6 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.1.6>`_\n-  `CodeIgniter v3.1.5 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.1.5>`_\n-  `CodeIgniter v3.1.4 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.1.4>`_\n-  `CodeIgniter v3.1.3 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.1.3>`_\n-  `CodeIgniter v3.1.2 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.1.2>`_\n-  `CodeIgniter v3.1.1 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.1.1>`_\n-  `CodeIgniter v3.1.0 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.1.0>`_\n-  `CodeIgniter v3.0.6 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.0.6>`_\n-  `CodeIgniter v3.0.5 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.0.5>`_\n-  `CodeIgniter v3.0.4 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.0.4>`_\n-  `CodeIgniter v3.0.3 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.0.3>`_\n-  `CodeIgniter v3.0.2 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.0.2>`_\n-  `CodeIgniter v3.0.1 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.0.1>`_\n-  `CodeIgniter v3.0.0 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.0.0>`_\n-  `CodeIgniter v2.2.6 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/2.2.6>`_\n-  `CodeIgniter v2.2.5 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/2.2.5>`_\n-  `CodeIgniter v2.2.4 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/2.2.4>`_\n-  `CodeIgniter v2.2.3 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/2.2.3>`_\n-  `CodeIgniter v2.2.2 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/2.2.2>`_\n-  `CodeIgniter v2.2.1 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/2.2.1>`_\n-  `CodeIgniter v2.2.0 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/2.2.0>`_\n-  `CodeIgniter v2.1.4 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/2.1.4>`_\n-  `CodeIgniter v2.1.3 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/2.1.3>`_\n-  `CodeIgniter v2.1.2 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/2.1.2>`_\n-  `CodeIgniter v2.1.1 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/2.1.1>`_\n-  `CodeIgniter v2.1.0 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/v2.1.0>`_\n\n******\nGitHub\n******\n\n`Git <https://git-scm.com/about>`_ is a distributed version control system.\n\nPublic Git access is available at `GitHub <https://github.com/bcit-ci/CodeIgniter>`_.\nPlease note that while every effort is made to keep this code base\nfunctional, we cannot guarantee the functionality of code taken from\nthe develop branch.\n\nBeginning with version 2.0.3, stable versions are also available via `GitHub Releases <https://github.com/bcit-ci/CodeIgniter/releases>`_.\n"
  },
  {
    "path": "user_guide_src/source/installation/index.rst",
    "content": "#########################\nInstallation Instructions\n#########################\n\nCodeIgniter is installed in four steps:\n\n#. Unzip the package.\n#. Upload the CodeIgniter folders and files to your server. Normally the\n   *index.php* file will be at your root.\n#. Open the *application/config/config.php* file with a text editor and\n   set your base URL. If you intend to use encryption or sessions, set\n   your encryption key.\n#. If you intend to use a database, open the\n   *application/config/database.php* file with a text editor and set your\n   database settings.\n\nIf you wish to increase security by hiding the location of your\nCodeIgniter files you can rename the system and application folders to\nsomething more private. If you do rename them, you must open your main\n*index.php* file and set the ``$system_path`` and ``$application_folder``\nvariables at the top of the file with the new name you've chosen.\n\nFor the best security, both the system and any application folders\nshould be placed above web root so that they are not directly accessible\nvia a browser. By default, *.htaccess* files are included in each folder\nto help prevent direct access, but it is best to remove them from public\naccess entirely in case the web server configuration changes or doesn't\nabide by the *.htaccess*.\n\nIf you would like to keep your views public it is also possible to move\nthe views folder out of your application folder.\n\nAfter moving them, open your main index.php file and set the\n``$system_path``, ``$application_folder`` and ``$view_folder`` variables,\npreferably with a full path, e.g. '*/www/MyUser/system*'.\n\nOne additional measure to take in production environments is to disable\nPHP error reporting and any other development-only functionality. In\nCodeIgniter, this can be done by setting the ``ENVIRONMENT`` constant, which\nis more fully described on the :doc:`security\npage <../general/security>`.\n\nThat's it!\n\nIf you're new to CodeIgniter, please read the :doc:`Getting\nStarted <../overview/getting_started>` section of the User Guide\nto begin learning how to build dynamic PHP applications. Enjoy!\n\n.. toctree::\n\t:hidden:\n\t:titlesonly:\n\n\tdownloads\n\tself\n\tupgrading\n\ttroubleshooting\n\n"
  },
  {
    "path": "user_guide_src/source/installation/troubleshooting.rst",
    "content": "###############\nTroubleshooting\n###############\n\nIf you find that no matter what you put in your URL only your default\npage is loading, it might be that your server does not support the\nREQUEST_URI variable needed to serve search-engine friendly URLs. As a\nfirst step, open your *application/config/config.php* file and look for\nthe URI Protocol information. It will recommend that you try a couple\nalternate settings. If it still doesn't work after you've tried this\nyou'll need to force CodeIgniter to add a question mark to your URLs. To\ndo this open your *application/config/config.php* file and change this::\n\n\t$config['index_page'] = \"index.php\";\n\nTo this::\n\n\t$config['index_page'] = \"index.php?\";\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_120.rst",
    "content": "####################################\nUpgrading From Beta 1.0 to Final 1.2\n####################################\n\nTo upgrade to Version 1.2 please replace the following directories with\nthe new versions:\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\n-  drivers\n-  helpers\n-  init\n-  language\n-  libraries\n-  plugins\n-  scaffolding\n\nPlease also replace your local copy of the user guide with the new\nversion.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_130.rst",
    "content": "#########################\nUpgrading from 1.2 to 1.3\n#########################\n\n.. note:: The instructions on this page assume you are running version\n\t1.2. If you have not upgraded to that version please do so first.\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace the following directories in your \"system\" folder with the new\nversions:\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\n-  application/**models**/ (new for 1.3)\n-  codeigniter (new for 1.3)\n-  drivers\n-  helpers\n-  init\n-  language\n-  libraries\n-  plugins\n-  scaffolding\n\nStep 2: Update your error files\n===============================\n\nVersion 1.3 contains two new error templates located in\napplication/errors, and for naming consistency the other error templates\nhave been renamed.\n\nIf you **have not** customized any of the error templates simply replace\nthis folder:\n\n-  application/errors/\n\nIf you **have** customized your error templates, rename them as follows:\n\n-  404.php = error_404.php\n-  error.php = error_general.php\n-  error_db.php (new)\n-  error_php.php (new)\n\nStep 3: Update your index.php file\n==================================\n\nPlease open your main index.php file (located at your root). At the very\nbottom of the file, change this::\n\n\trequire_once BASEPATH.'libraries/Front_controller'.EXT;\n\nTo this::\n\n\trequire_once BASEPATH.'codeigniter/CodeIgniter'.EXT;\n\nStep 4: Update your config.php file\n===================================\n\nOpen your application/config/config.php file and add these new items::\n\n\n    /*\n    |------------------------------------------------\n    | URL suffix\n    |------------------------------------------------\n    |\n    | This option allows you to add a suffix to all URLs.\n    | For example, if a URL is this:\n    |\n    | example.com/index.php/products/view/shoes\n    |\n    | You can optionally add a suffix, like \".html\",\n    | making the page appear to be of a certain type:\n    |\n    | example.com/index.php/products/view/shoes.html\n    |\n    */\n    $config['url_suffix'] = \"\";\n\n\n    /*\n    |------------------------------------------------\n    | Enable Query Strings\n    |------------------------------------------------\n    |\n    | By default CodeIgniter uses search-engine and\n    | human-friendly segment based URLs:\n    |\n    | example.com/who/what/where/\n    |\n    | You can optionally enable standard query string\n    | based URLs:\n    |\n    | example.com?who=me&what=something&where=here\n    |\n    | Options are: TRUE or FALSE (boolean)\n    |\n    | The two other items let you set the query string \"words\"\n    | that will invoke your controllers and functions:\n    | example.com/index.php?c=controller&m=function\n    |\n    */\n    $config['enable_query_strings'] = FALSE;\n    $config['controller_trigger'] = 'c';\n    $config['function_trigger'] = 'm';\n\nStep 5: Update your database.php file\n=====================================\n\nOpen your application/config/database.php file and add these new items::\n\n\n    $db['default']['dbprefix'] = \"\";\n    $db['default']['active_r'] = TRUE;\n\nStep 6: Update your user guide\n==============================\n\nPlease also replace your local copy of the user guide with the new\nversion.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_131.rst",
    "content": "###########################\nUpgrading from 1.3 to 1.3.1\n###########################\n\n.. note:: The instructions on this page assume you are running version\n\t1.3. If you have not upgraded to that version please do so first.\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace the following directories in your \"system\" folder with the new\nversions:\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\n-  drivers\n-  init/init_unit_test.php (new for 1.3.1)\n-  language/\n-  libraries\n-  scaffolding\n\nStep 2: Update your user guide\n==============================\n\nPlease also replace your local copy of the user guide with the new\nversion.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_132.rst",
    "content": "#############################\nUpgrading from 1.3.1 to 1.3.2\n#############################\n\n.. note:: The instructions on this page assume you are running version\n\t1.3.1. If you have not upgraded to that version please do so first.\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace the following directories in your \"system\" folder with the new\nversions:\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\n-  drivers\n-  init\n-  libraries\n\nStep 2: Update your user guide\n==============================\n\nPlease also replace your local copy of the user guide with the new\nversion.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_133.rst",
    "content": "#############################\nUpgrading from 1.3.2 to 1.3.3\n#############################\n\n.. note:: The instructions on this page assume you are running version\n\t1.3.2. If you have not upgraded to that version please do so first.\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace the following directories in your \"system\" folder with the new\nversions:\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\n-  codeigniter\n-  drivers\n-  helpers\n-  init\n-  libraries\n\nStep 2: Update your Models\n==========================\n\nIf you are **NOT** using CodeIgniter's\n:doc:`Models <../general/models>` feature disregard this step.\n\nAs of version 1.3.3, CodeIgniter does **not** connect automatically to\nyour database when a model is loaded. This allows you greater\nflexibility in determining which databases you would like used with your\nmodels. If your application is not connecting to your database prior to\na model being loaded you will have to update your code. There are\nseveral options for connecting, :doc:`as described\nhere <../general/models>`.\n\nStep 3: Update your user guide\n==============================\n\nPlease also replace your local copy of the user guide with the new\nversion.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_140.rst",
    "content": "#############################\nUpgrading from 1.3.3 to 1.4.0\n#############################\n\n.. note:: The instructions on this page assume you are running version\n\t1.3.3. If you have not upgraded to that version please do so first.\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace the following directories in your \"system\" folder with the new\nversions:\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\n-  application/config/**hooks.php**\n-  application/config/**mimes.php**\n-  codeigniter\n-  drivers\n-  helpers\n-  init\n-  language\n-  libraries\n-  scaffolding\n\nStep 2: Update your config.php file\n===================================\n\nOpen your application/config/config.php file and add these new items::\n\n\n\n    /*\n    |--------------------------------------------------------------------------\n    | Enable/Disable System Hooks\n    |--------------------------------------------------------------------------\n    |\n    | If you would like to use the \"hooks\" feature you must enable it by\n    | setting this variable to TRUE (boolean).  See the user guide for details.\n    |\n    */\n    $config['enable_hooks'] = FALSE;\n\n\n    /*\n    |--------------------------------------------------------------------------\n    | Allowed URL Characters\n    |--------------------------------------------------------------------------\n    |\n    | This lets you specify which characters are permitted within your URLs.\n    | When someone tries to submit a URL with disallowed characters they will\n    | get a warning message.\n    |\n    | As a security measure you are STRONGLY encouraged to restrict URLs to\n    | as few characters as possible.  By default only these are allowed: a-z 0-9~%.:_-\n    |\n    | Leave blank to allow all characters -- but only if you are insane.\n    |\n    | DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!!\n    |\n    */\n    $config['permitted_uri_chars'] = 'a-z 0-9~%.:_-';\n\nStep 3: Update your user guide\n==============================\n\nPlease also replace your local copy of the user guide with the new\nversion.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_141.rst",
    "content": "#############################\nUpgrading from 1.4.0 to 1.4.1\n#############################\n\n.. note:: The instructions on this page assume you are running version\n\t1.4.0. If you have not upgraded to that version please do so first.\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace the following directories in your \"system\" folder with the new\nversions:\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\n-  codeigniter\n-  drivers\n-  helpers\n-  libraries\n\nStep 2: Update your config.php file\n===================================\n\nOpen your application/config/config.php file and add this new item::\n\n\n\n    /*\n    |--------------------------------------------------------------------------\n    | Output Compression\n    |--------------------------------------------------------------------------\n    |\n    | Enables Gzip output compression for faster page loads.  When enabled,\n    | the output class will test whether your server supports Gzip.\n    | Even if it does, however, not all browsers support compression\n    | so enable only if you are reasonably sure your visitors can handle it.\n    |\n    | VERY IMPORTANT:  If you are getting a blank page when compression is enabled it\n    | means you are prematurely outputting something to your browser. It could\n    | even be a line of whitespace at the end of one of your scripts.  For\n    | compression to work, nothing can be sent before the output buffer is called\n    | by the output class.  Do not \"echo\" any values with compression enabled.\n    |\n    */\n    $config['compress_output'] = FALSE;\n\nStep 3: Rename an Autoload Item\n===============================\n\nOpen the following file: application/config/autoload.php\n\nFind this array item::\n\n\t$autoload['core'] = array();\n\nAnd rename it to this::\n\n\t$autoload['libraries'] = array();\n\nThis change was made to improve clarity since some users were not sure\nthat their own libraries could be auto-loaded.\n\nStep 4: Update your user guide\n==============================\n\nPlease also replace your local copy of the user guide with the new\nversion.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_150.rst",
    "content": "#############################\nUpgrading from 1.4.1 to 1.5.0\n#############################\n\n.. note:: The instructions on this page assume you are running version\n\t1.4.1. If you have not upgraded to that version please do so first.\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace these files and directories in your \"system\" folder with the new\nversions:\n\n-  application/config/user_agents.php (new file for 1.5)\n-  application/config/smileys.php (new file for 1.5)\n-  codeigniter/\n-  database/ (new folder for 1.5. Replaces the \"drivers\" folder)\n-  helpers/\n-  language/\n-  libraries/\n-  scaffolding/\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\nStep 2: Update your database.php file\n=====================================\n\nOpen your application/config/database.php file and add these new items::\n\n\n    $db['default']['cache_on'] = FALSE;\n    $db['default']['cachedir'] = '';\n\nStep 3: Update your config.php file\n===================================\n\nOpen your application/config/config.php file and ADD these new items::\n\n\n    /*\n    |--------------------------------------------------------------------------\n    | Class Extension Prefix\n    |--------------------------------------------------------------------------\n    |\n    | This item allows you to set the filename/classname prefix when extending\n    | native libraries.  For more information please see the user guide:\n    |\n    | https://codeigniter.com/userguide3/general/core_classes.html\n    | https://codeigniter.com/userguide3/general/creating_libraries.html\n    |\n    */\n    $config['subclass_prefix'] = 'MY_';\n\n    /*\n    |--------------------------------------------------------------------------\n    | Rewrite PHP Short Tags\n    |--------------------------------------------------------------------------\n    |\n    | If your PHP installation does not have short tag support enabled CI\n    | can rewrite the tags on-the-fly, enabling you to utilize that syntax\n    | in your view files.  Options are TRUE or FALSE (boolean)\n    |\n    */\n    $config['rewrite_short_tags'] = FALSE;\n\nIn that same file REMOVE this item::\n\n\n    /*\n    |--------------------------------------------------------------------------\n    | Enable/Disable Error Logging\n    |--------------------------------------------------------------------------\n    |\n    | If you would like errors or debug messages logged set this variable to\n    | TRUE (boolean).  Note: You must set the file permissions on the \"logs\" folder\n    | such that it is writable.\n    |\n    */\n    $config['log_errors'] = FALSE;\n\nError logging is now disabled simply by setting the threshold to zero.\n\nStep 4: Update your main index.php file\n=======================================\n\nIf you are running a stock index.php file simply replace your version\nwith the new one.\n\nIf your index.php file has internal modifications, please add your\nmodifications to the new file and use it.\n\nStep 5: Update your user guide\n==============================\n\nPlease also replace your local copy of the user guide with the new\nversion.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_152.rst",
    "content": "#############################\nUpgrading from 1.5.0 to 1.5.2\n#############################\n\n.. note:: The instructions on this page assume you are running version\n\t1.5.0 or 1.5.1. If you have not upgraded to that version please do so\n\tfirst.\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace these files and directories in your \"system\" folder with the new\nversions:\n\n-  system/helpers/download_helper.php\n-  system/helpers/form_helper.php\n-  system/libraries/Table.php\n-  system/libraries/User_agent.php\n-  system/libraries/Exceptions.php\n-  system/libraries/Input.php\n-  system/libraries/Router.php\n-  system/libraries/Loader.php\n-  system/libraries/Image_lib.php\n-  system/language/english/unit_test_lang.php\n-  system/database/DB_active_rec.php\n-  system/database/drivers/mysqli/mysqli_driver.php\n-  codeigniter/\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\nStep 2: Update your user guide\n==============================\n\nPlease also replace your local copy of the user guide with the new\nversion.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_153.rst",
    "content": "#############################\nUpgrading from 1.5.2 to 1.5.3\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace these files and directories in your \"system\" folder with the new\nversions:\n\n-  system/database/drivers\n-  system/helpers\n-  system/libraries/Input.php\n-  system/libraries/Loader.php\n-  system/libraries/Profiler.php\n-  system/libraries/Table.php\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\nStep 2: Update your user guide\n==============================\n\nPlease also replace your local copy of the user guide with the new\nversion.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_154.rst",
    "content": "#############################\nUpgrading from 1.5.3 to 1.5.4\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace these files and directories in your \"system\" folder with the new\nversions:\n\n-  application/config/mimes.php\n-  system/codeigniter\n-  system/database\n-  system/helpers\n-  system/libraries\n-  system/plugins\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\nStep 2: Add charset to your config.php\n======================================\n\nAdd the following to application/config/config.php\n\n::\n\n\t/*\n\t|--------------------------------------------------------------------------\n\t| Default Character Set\n\t|--------------------------------------------------------------------------\n\t|\n\t| This determines which character set is used by default in various methods\n\t| that require a character set to be provided.\n\t|\n\t*/\n\t$config['charset'] = \"UTF-8\";\n\nStep 3: Autoloading language files\n==================================\n\nIf you want to autoload any language files, add this line to\napplication/config/autoload.php\n\n::\n\n\t$autoload['language'] = array();\n\nStep 4: Update your user guide\n==============================\n\nPlease also replace your local copy of the user guide with the new\nversion.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_160.rst",
    "content": "#############################\nUpgrading from 1.5.4 to 1.6.0\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace these files and directories in your \"system\" folder with the new\nversions:\n\n-  system/codeigniter\n-  system/database\n-  system/helpers\n-  system/libraries\n-  system/plugins\n-  system/language\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\nStep 2: Add time_to_update to your config.php\n===============================================\n\nAdd the following to application/config/config.php with the other\nsession configuration options\n\n::\n\n\t$config['sess_time_to_update']         = 300;\n\n\nStep 3: Add $autoload['model']\n==============================\n\nAdd the following to application/config/autoload.php\n\n::\n\n\t/*\n\t| -------------------------------------------------------------------\n\t| Auto-load Model files\n\t| -------------------------------------------------------------------\n\t| Prototype:\n\t|\n\t| $autoload['model'] = array('my_model');\n\t|\n\t*/\n\n\t$autoload['model'] = array();\n\n\nStep 4: Add to your database.php\n================================\n\nMake the following changes to your application/config/database.php file:\n\nAdd the following variable above the database configuration options,\nwith $active_group\n\n::\n\n\t$active_record = TRUE;\n\n\nRemove the following from your database configuration options\n\n::\n\n\t$db['default']['active_r'] = TRUE;\n\n\nAdd the following to your database configuration options\n\n::\n\n\t$db['default']['char_set'] = \"utf8\";\n\t$db['default']['dbcollat'] = \"utf8_general_ci\";\n\n\nStep 5: Update your user guide\n==============================\n\nPlease also replace your local copy of the user guide with the new\nversion.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_161.rst",
    "content": "#############################\nUpgrading from 1.6.0 to 1.6.1\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace these files and directories in your \"system\" folder with the new\nversions:\n\n-  system/codeigniter\n-  system/database\n-  system/helpers\n-  system/language\n-  system/libraries\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\nStep 2: Update your user guide\n==============================\n\nPlease also replace your local copy of the user guide with the new\nversion.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_162.rst",
    "content": "#############################\nUpgrading from 1.6.1 to 1.6.2\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace these files and directories in your \"system\" folder with the new\nversions:\n\n-  system/codeigniter\n-  system/database\n-  system/helpers\n-  system/language\n-  system/libraries\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\nStep 2: Encryption Key\n======================\n\nIf you are using sessions, open up application/config/config.php and\nverify you've set an encryption key.\n\nStep 3: Constants File\n======================\n\nCopy /application/config/constants.php to your installation, and modify\nif necessary.\n\nStep 4: Mimes File\n==================\n\nReplace /application/config/mimes.php with the dowloaded version. If\nyou've added custom mime types, you'll need to re-add them.\n\nStep 5: Update your user guide\n==============================\n\nPlease also replace your local copy of the user guide with the new\nversion.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_163.rst",
    "content": "#############################\nUpgrading from 1.6.2 to 1.6.3\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace these files and directories in your \"system\" folder with the new\nversions:\n\n-  system/codeigniter\n-  system/database\n-  system/helpers\n-  system/language\n-  system/libraries\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\nStep 2: Update your user guide\n==============================\n\nPlease also replace your local copy of the user guide with the new\nversion.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_170.rst",
    "content": "#############################\nUpgrading from 1.6.3 to 1.7.0\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace these files and directories in your \"system\" folder with the new\nversions:\n\n-  system/codeigniter\n-  system/database\n-  system/helpers\n-  system/language\n-  system/libraries\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\nStep 2: Update your Session Table\n=================================\n\nIf you are using the Session class in your application, AND if you are\nstoring session data to a database, you must add a new column named\nuser_data to your session table. Here is an example of what this column\nmight look like for MySQL::\n\n\tuser_data text NOT NULL\n\nTo add this column you will run a query similar to this::\n\n\tALTER TABLE `ci_sessions` ADD `user_data` text NOT NULL\n\nYou'll find more information regarding the new Session functionality in\nthe :doc:`Session class <../libraries/sessions>` page.\n\nStep 3: Update your Validation Syntax\n=====================================\n\nThis is an **optional**, but recommended step, for people currently\nusing the Validation class. CI 1.7 introduces a new :doc:`Form Validation\nclass <../libraries/form_validation>`, which deprecates the old\nValidation library. We have left the old one in place so that existing\napplications that use it will not break, but you are encouraged to\nmigrate to the new version as soon as possible. Please read the user\nguide carefully as the new library works a little differently, and has\nseveral new features.\n\nStep 4: Update your user guide\n==============================\n\nPlease replace your local copy of the user guide with the new version,\nincluding the image files.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_171.rst",
    "content": "#############################\nUpgrading from 1.7.0 to 1.7.1\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace these files and directories in your \"system\" folder with the new\nversions:\n\n-  system/codeigniter\n-  system/database\n-  system/helpers\n-  system/language\n-  system/libraries\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\nStep 2: Update your user guide\n==============================\n\nPlease replace your local copy of the user guide with the new version,\nincluding the image files.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_172.rst",
    "content": "#############################\nUpgrading from 1.7.1 to 1.7.2\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace these files and directories in your \"system\" folder with the new\nversions:\n\n-  system/codeigniter\n-  system/database\n-  system/helpers\n-  system/language\n-  system/libraries\n-  index.php\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\nStep 2: Remove header() from 404 error template\n===============================================\n\nIf you are using header() in your 404 error template, such as the case\nwith the default error_404.php template shown below, remove that line\nof code.\n\n::\n\n\t<?php header(\"HTTP/1.1 404 Not Found\"); ?>\n\n404 status headers are now properly handled in the show_404() method\nitself.\n\nStep 3: Confirm your system_path\n=================================\n\nIn your updated index.php file, confirm that the $system_path variable\nis set to your application's system folder.\n\nStep 4: Update your user guide\n==============================\n\nPlease replace your local copy of the user guide with the new version,\nincluding the image files.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_200.rst",
    "content": "#############################\nUpgrading from 1.7.2 to 2.0.0\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\n*******************\nUpdate Instructions\n*******************\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your \"system\" folder **except**\nyour application folder.\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\nStep 2: Adjust get_dir_file_info() where necessary\n=====================================================\n\nVersion 2.0.0 brings a non-backwards compatible change to\nget_dir_file_info() in the :doc:`File\nHelper <../helpers/file_helper>`. Non-backwards compatible changes\nare extremely rare in CodeIgniter, but this one we feel was warranted\ndue to how easy it was to create serious server performance issues. If\nyou *need* recursiveness where you are using this helper function,\nchange such instances, setting the second parameter, $top_level_only\nto FALSE::\n\n\tget_dir_file_info('/path/to/directory', FALSE);\n\nStep 3: Convert your Plugins to Helpers\n=======================================\n\n2.0.0 gets rid of the \"Plugin\" system as their functionality was\nidentical to Helpers, but non-extensible. You will need to rename your\nplugin files from filename_pi.php to filename_helper.php, move them to\nyour helpers folder, and change all instances of::\n\n\t$this->load->plugin('foo');\n\nto ::\n\n\t$this->load->helper('foo');\n\n\nStep 4: Update stored encrypted data\n====================================\n\n.. note:: If your application does not use the Encrypt library, does\n\tnot store Encrypted data permanently, or is on an environment that does\n\tnot support Mcrypt, you may skip this step.\n\nThe Encrypt library has had a number of improvements, some for\nencryption strength and some for performance, that has an unavoidable\nconsequence of making it no longer possible to decode encrypted data\nproduced by the original version of this library. To help with the\ntransition, a new method has been added, encode_from_legacy() that\nwill decode the data with the original algorithm and return a re-encoded\nstring using the improved methods. This will enable you to easily\nreplace stale encrypted data with fresh in your applications, either on\nthe fly or en masse.\n\nPlease read how to use this in the Encrypt library documentation.\n\nStep 5: Remove loading calls for the compatibility helper.\n==========================================================\n\nThe compatibility helper has been removed from the CodeIgniter core. All\nmethods in it should be natively available in supported PHP versions.\n\nStep 6: Update Class extension\n==============================\n\nAll core classes are now prefixed with CI\\_. Update Models and\nControllers to extend CI_Model and CI_Controller, respectively.\n\nStep 7: Update Parent Constructor calls\n=======================================\n\nAll native CodeIgniter classes now use the PHP 5 \\__construct()\nconvention. Please update extended libraries to call\nparent::\\__construct().\n\nStep 8: Move any core extensions to application/core\n====================================================\n\nAny extensions to core classes (e.g. MY_Controller.php) in your\napplication/libraries folder must be moved to the new \napplication/core folder.\n\nStep 9: Update your user guide\n==============================\n\nPlease replace your local copy of the user guide with the new version,\nincluding the image files.\n\n\n************\nUpdate Notes\n************\n\nPlease refer to the :ref:`2.0.0 Change Log <2.0.0-changelog>` for full\ndetails, but here are some of the larger changes that are more likely to\nimpact your code:\n\n- Scaffolding has been removed.\n- The CAPTCHA plugin in now a :doc:`helper </helpers/captcha_helper>`.\n- The JavaScript calendar plugin was removed.\n- The *system/cache* and *system/logs* directories are now in the application\n  directory.\n- The Validation class has been removed.  Please see the\n  :doc:`Form Validation library </libraries/form_validation>`\n- \"default\" is now a reserved name.\n- The xss_clean() function has moved to the :doc:`Security Class\n  </libraries/security>`.\n- do_xss_clean() now returns FALSE if the uploaded file fails XSS checks.\n- The :doc:`Session Class </libraries/sessions>` requires now the use of an\n  encryption key set in the config file.\n- The following deprecated Active Record functions have been removed:\n  ``orwhere``, ``orlike``, ``groupby``, ``orhaving``, ``orderby``,\n  ``getwhere``.\n- ``_drop_database()`` and ``_create_database()`` functions have been removed\n  from the db utility drivers.\n- The ``dohash()`` function of the :doc:`Security helper\n  </helpers/security_helper>`\n  has been renamed to ``do_hash()`` for naming consistency.\n\nThe config folder\n=================\n\nThe following files have been changed:\n\n- config.php\n- database.php\n- mimes.php\n- routes.php\n- user_agents.php\n\nThe following files have been added:\n\n- foreign_chars.php\n- profiler.php\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_201.rst",
    "content": "#############################\nUpgrading from 2.0.0 to 2.0.1\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your \"system\" folder and replace\nyour index.php file. If any modifications were made to your index.php\nthey will need to be made fresh in this new one.\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\nStep 2: Replace config/mimes.php\n================================\n\nThis config file has been updated to contain more mime types, please\ncopy it to application/config/mimes.php.\n\nStep 3: Check for forms posting to default controller\n=====================================================\n\nThe default behavior for form_open() when called with no parameters\nused to be to post to the default controller, but it will now just leave\nan empty action=\"\" meaning the form will submit to the current URL. If\nsubmitting to the default controller was the expected behavior it will\nneed to be changed from::\n\n\techo form_open(); //<form action=\"\" method=\"post\" accept-charset=\"utf-8\">\n\nto use either a / or base_url()::\n\n\techo form_open('/'); //<form action=\"http://example.com/index.php/\" method=\"post\" accept-charset=\"utf-8\">\n\techo form_open(base_url()); //<form action=\"http://example.com/\" method=\"post\" accept-charset=\"utf-8\">\n\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_202.rst",
    "content": "#############################\nUpgrading from 2.0.1 to 2.0.2\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your \"system\" folder and replace\nyour index.php file. If any modifications were made to your index.php\nthey will need to be made fresh in this new one.\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\nStep 2: Remove loading calls for the Security Library\n=====================================================\n\nSecurity has been moved to the core and is now always loaded\nautomatically. Make sure you remove any loading calls as they will\nresult in PHP errors.\n\nStep 3: Move MY_Security\n=========================\n\nIf you are overriding or extending the Security library, you will need\nto move it to application/core.\n\ncsrf_token_name and csrf_hash have changed to protected class\nproperties. Please use security->get_csrf_hash() and\nsecurity->get_csrf_token_name() to access those values.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_203.rst",
    "content": "#############################\nUpgrading from 2.0.2 to 2.0.3\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your \"system\" folder and replace\nyour index.php file. If any modifications were made to your index.php\nthey will need to be made fresh in this new one.\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\nStep 2: Update your main index.php file\n=======================================\n\nIf you are running a stock index.php file simply replace your version\nwith the new one.\n\nIf your index.php file has internal modifications, please add your\nmodifications to the new file and use it.\n\nStep 3: Replace config/user_agents.php\n=======================================\n\nThis config file has been updated to contain more user agent types,\nplease copy it to application/config/user_agents.php.\n\nStep 4: Change references of the EXT constant to \".php\"\n=======================================================\n\n.. note:: The EXT Constant has been marked as deprecated, but has not\n\tbeen removed from the application. You are encouraged to make the\n\tchanges sooner rather than later.\n\nStep 5: Remove APPPATH.'third_party' from autoload.php\n=======================================================\n\nOpen application/config/autoload.php, and look for the following::\n\n\t$autoload['packages'] = array(APPPATH.'third_party');\n\nIf you have not chosen to load any additional packages, that line can be\nchanged to::\n\n\t$autoload['packages'] = array();\n\nWhich should provide for nominal performance gains if not autoloading\npackages.\n\nUpdate Sessions Database Tables\n===============================\n\nIf you are using database sessions with the CI Session Library, please\nupdate your ci_sessions database table as follows::\n\n\tCREATE INDEX last_activity_idx ON ci_sessions(last_activity);\n\tALTER TABLE ci_sessions MODIFY user_agent VARCHAR(120);\n\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_210.rst",
    "content": "#############################\nUpgrading from 2.0.3 to 2.1.0\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your \"system\" folder.\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\nStep 2: Replace config/mimes.php\n================================\n\nThis config file has been updated to contain more user agent types,\nplease copy it to *application/config/mimes.php*.\n\nStep 3: Update your user guide\n==============================\n\nPlease also replace your local copy of the user guide with the new\nversion.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_211.rst",
    "content": "#############################\nUpgrading from 2.1.0 to 2.1.1\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your \"system\" folder.\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\nStep 2: Replace config/mimes.php\n================================\n\nThis config file has been updated to contain more user mime-types, please copy\nit to _application/config/mimes.php*.\n\nStep 3: Update your IP address tables\n=====================================\n\nThis upgrade adds support for IPv6 IP addresses. In order to store them, you need\nto enlarge your ip_address columns to 45 characters. For example, CodeIgniter's\nsession table will need to change\n\n::\n\n\tALTER TABLE ci_sessions CHANGE ip_address ip_address varchar(45) default '0' NOT NULL"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_212.rst",
    "content": "#############################\nUpgrading from 2.1.1 to 2.1.2\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your \"system\" folder.\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\nStep 2: Update your user guide\n==============================\n\nPlease also replace your local copy of the user guide with the new\nversion."
  },
  {
    "path": "user_guide_src/source/installation/upgrade_213.rst",
    "content": "#############################\nUpgrading from 2.1.2 to 2.1.3\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your \"system\" folder.\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\nStep 2: Update your user guide\n==============================\n\nPlease also replace your local copy of the user guide with the new\nversion."
  },
  {
    "path": "user_guide_src/source/installation/upgrade_214.rst",
    "content": "#############################\nUpgrading from 2.1.3 to 2.1.4\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your \"system\" folder.\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first."
  },
  {
    "path": "user_guide_src/source/installation/upgrade_220.rst",
    "content": "#############################\nUpgrading from 2.1.4 to 2.2.x\n#############################\n\n.. note:: The **Encrypt Class** now requires the Mcrypt extension. If you\n\twere previously using the Encrypt Class without Mcrypt, then this\n\tis a breaking change.  You must install the Mcrypt extension in\n\torder to upgrade. For information on installing Mcrypt please see\n\tthe PHP `documentation <https://secure.php.net/manual/en/mcrypt.setup.php>`.\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your \"system\" folder.\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_221.rst",
    "content": "#############################\nUpgrading from 2.2.0 to 2.2.1\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your \"system\" folder.\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first."
  },
  {
    "path": "user_guide_src/source/installation/upgrade_222.rst",
    "content": "#############################\nUpgrading from 2.2.1 to 2.2.2\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your \"system\" folder.\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first."
  },
  {
    "path": "user_guide_src/source/installation/upgrade_223.rst",
    "content": "#############################\nUpgrading from 2.2.2 to 2.2.3\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your \"system\" folder.\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first."
  },
  {
    "path": "user_guide_src/source/installation/upgrade_300.rst",
    "content": "#############################\nUpgrading from 2.2.x to 3.0.x\n#############################\n\nBefore performing an update you should take your site offline by replacing the index.php file with a static one.\n\n*************************************\nStep 1: Update your CodeIgniter files\n*************************************\n\n**Replace** all files and directories in your *system/* directory and\nreplace your index.php file. If any modifications were made to your\nindex.php they will need to be made fresh in this new one.\n\n.. important:: You have to delete the old *system/* directory first and\n\tthen put the new one in its place. A simple copy-paste may cause\n\tissues.\n\n.. note:: If you have any custom developed files in these folders please\n\tmake copies of them first.\n\n**************************************\nStep 2: Update your classes file names\n**************************************\n\nStarting with CodeIgniter 3.0, all class filenames (libraries, drivers, controllers\nand models) must be named in a Ucfirst-like manner or in other words - they must\nstart with a capital letter.\n\nFor example, if you have the following library file:\n\n\tapplication/libraries/mylibrary.php\n\n... then you'll have to rename it to:\n\n\tapplication/libraries/Mylibrary.php\n\nThe same goes for driver libraries and extensions and/or overrides of CodeIgniter's\nown libraries and core classes.\n\n\tapplication/libraries/MY_email.php  \n\tapplication/core/MY_log.php\n\nThe above files should respectively be renamed to the following:\n\n\tapplication/libraries/MY_Email.php  \n\tapplication/core/MY_Log.php\n\nControllers:\n\n\tapplication/controllers/welcome.php\t->\tapplication/controllers/Welcome.php\n\nModels:\n\n\tapplication/models/misc_model.php\t->\tapplication/models/Misc_model.php\n\nPlease note that this DOES NOT affect directories, configuration files, views,\nhelpers, hooks and anything else - it is only applied to classes.\n\nYou must now follow just one simple rule - class names in Ucfirst and everything else\nin lowercase.\n\n********************************\nStep 3: Replace config/mimes.php\n********************************\n\nThis config file has been updated to contain more user mime-types, please copy\nit to *application/config/mimes.php*.\n\n**************************************************************\nStep 4: Remove $autoload['core'] from your config/autoload.php\n**************************************************************\n\nUse of the ``$autoload['core']`` config array has been deprecated as of CodeIgniter 1.4.1 and is now removed.\nMove any entries that you might have listed there to ``$autoload['libraries']`` instead.\n\n***************************************************\nStep 5: Move your Log class overrides or extensions\n***************************************************\n\nThe Log Class is considered as a \"core\" class and is now located in the\n**system/core/** directory. Therefore, in order for your Log class overrides\nor extensions to work, you need to move them to **application/core/**:\n\n\tapplication/libraries/Log.php -> application/core/Log.php  \n\tapplication/libraries/MY_Log.php -> application/core/MY_Log.php\n\n*****************************************\nStep 6: Update your Session library usage\n*****************************************\n\nThe :doc:`Session Library </libraries/sessions>` has been completely\nre-written in CodeIgniter 3 and now comes with a bunch of new features,\nbut that also means that there are changes that you should make ...\n\nMost notably, the library now uses separate storage drivers instead of\nalways relying on (encrypted) cookies.\nIn fact, cookies as storage have now been removed and you must always use\nsome kind of server-side storage engine, with the file-system being the\ndefault option.\n\nThe Session Class now utilizes PHP's own mechanisms for building custom\nsession handlers, which also means that your session data is now\naccessible via the ``$_SESSION`` superglobal (though, we've kept the\npossibility to use it as \"userdata\", like you've done until now).\n\nA few configuration options have been removed and a few have been added.\nYou should really read the whole :doc:`Session library manual\n</libraries/sessions>` for the details, but here's a short list of changes\nthat you should make:\n\n  - Set your ``$config['sess_driver']`` value\n\n    It will default to 'files', unless you've previously used\n    ``$config['sess_use_database']``, in which case it will be set to\n    'database'.\n\n  - Set a ``$config['sess_save_path']`` value\n\n    For the 'database' driver, a fallback to ``$config['sess_table_name']``\n    is in place, but otherwise requires you to read the manual for the\n    specific driver of your choice.\n\n  - Update your ``ci_sessions`` table ('database' driver only)\n\n    The table structure has changed a bit, and more specifically:\n\n      - ``session_id`` field is renamed to ``id``\n      - ``user_agent`` field is dropped\n      - ``user_data`` field is renamed to ``data`` and under MySQL is now of type BLOB\n      - ``last_activity`` field is renamed to ``timestamp``\n\n    This is accompanied by a slight change in the table indexes too, so\n    please read the manual about the `Session Database Driver\n    <../libraries/sessions.html#database-driver>`_ for more information.\n\n    .. important:: Only MySQL and PostgreSQL are officially supported\n    \tnow. Other databases may still work, but due to lack of advisory\n    \tlocking features, they are unsafe for concurrent requests and\n    \tyou should consider using another driver instead.\n\n  - Remove ``$config['sess_match_useragent']``\n\n    The user-agent string is input supplied by the user's browser, or in\n    other words: client side input. As such, it is an ineffective feature\n    and hence why it has been removed.\n\n  - Remove ``$config['sess_encrypt_cookie']``\n\n    As already noted, the library no longer uses cookies as a storage\n    mechanism, which renders this option useless.\n\n  - Remove ``$config['sess_expire_on_close']``\n\n    This option is still usable, but only for backwards compatibility\n    purposes and it should be otherwise removed. The same effect is\n    achieved by setting ``$config['sess_expiration']`` to 0.\n\n  - Check \"flashdata\" for collisions with \"userdata\"\n\n    Flashdata is now just regular \"userdata\", only marked for deletion on\n    the next request. In other words: you can't have both \"userdata\" and\n    \"flashdata\" with the same name, because it's the same thing.\n\n  - Check usage of session metadata\n\n    Previously, you could access the 'session_id', 'ip_address',\n    'user_agent' and 'last_activity' metadata items as userdata.\n    This is no longer possible, and you should read the notes about\n    `Session Metadata <../libraries/sessions.html#accessing-session-metadata>`_\n    if your application relies on those values.\n\n  - Check ``unset_userdata()`` usage\n\n    Previously, this method used to accept an associative array of\n    ``'key' => 'dummy value'`` pairs for unsetting multiple keys. That\n    however makes no sense and you now have to pass *only* the keys, as\n    the elements of an array.\n\n    ::\n\n    \t// Old\n    \t$this->session->unset_userdata(array('item' => '', 'item2' => ''));\n\n    \t// New\n    \t$this->session->unset_userdata(array('item', 'item2'));\n\nFinally, if you have written a Session extension, you must now move it to\nthe *application/libraries/Session/* directory, although chances are that\nit will now also have to be re-factored.\n\n***************************************\nStep 7: Update your config/database.php\n***************************************\n\nDue to 3.0.0's renaming of Active Record to Query Builder, inside your\n**config/database.php**, you will need to rename the ``$active_record``\nvariable to ``$query_builder``::\n\n\t$active_group = 'default';\n\t// $active_record = TRUE;\n\t$query_builder = TRUE;\n\n************************************\nStep 8: Replace your error templates\n************************************\n\nIn CodeIgniter 3.0, the error templates are now considered as views and have been moved to the\n*application/views/errors* directory.\n\nFurthermore, we've added support for CLI error templates in plain-text format that unlike HTML,\nis suitable for the command line. This of course requires another level of separation.\n\nIt is safe to move your old templates from *application/errors* to *application/views/errors/html*,\nbut you'll have to copy the new *application/views/errors/cli* directory from the CodeIgniter archive.\n\n******************************************\nStep 9: Update your config/routes.php file\n******************************************\n\nRoutes containing :any\n======================\n\nHistorically, CodeIgniter has always provided the **:any** wildcard in\nrouting, with the intention of providing a way to match any character\n**within** an URI segment.\n\nHowever, the **:any** wildcard is actually just an alias for a regular\nexpression and used to be executed in that manner as **.+**. This is\nconsidered a bug, as it also matches the / (forward slash) character, which\nis the URI segment delimiter and that was never the intention.\n\nIn CodeIgniter 3, the **:any** wildcard will now represent **[^/]+**, so\nthat it will not match a forward slash.\n\nThere are certainly many developers that have utilized this bug as an actual\nfeature. If you're one of them and want to match a forward slash, please use\nthe **.+** regular expression::\n\n\t(.+)\t// matches ANYTHING\n\t(:any)\t// matches any character, except for '/'\n\nDirectories and 'default_controller', '404_override'\n====================================================\n\nAs you should know, the ``$route['default_controller']`` and\n``$route['404_override']`` settings accept not only a controller name, but\nalso *controller/method* pairs. However, a bug in the routing logic has\nmade it possible for some users to use that as *directory/controller*\ninstead.\n\nAs already said, this behavior was incidental and was never intended, nor\ndocumented. If you've relied on it, your application will break with\nCodeIgniter 3.0.\n\nAnother notable change in version 3 is that 'default_controller' and\n'404_override' are now applied *per directory*. To explain what this means,\nlet's take the following example::\n\n\t$route['default_controller'] = 'main';\n\nNow, assuming that your website is located at *example.com*, you already\nknow that if a user visits ``http://example.com/``, the above setting will\ncause your 'Main' controller to be loaded.\n\nHowever, what happens if you have an *application/controllers/admin/*\ndirectory and the user visits ``http://example.com/admin/``?\nIn CodeIgniter 3, the router will look for a 'Main' controller under the\nadmin/ directory as well. If not found, a Not Found (404) will be triggered.\n\nThe same rule applies to the '404_override' setting.\n\n*************************************************************************\nStep 10: Many functions now return NULL instead of FALSE on missing items\n*************************************************************************\n\nMany methods and functions now return NULL instead of FALSE when the required items don't exist:\n\n - :doc:`Common functions <../general/common_functions>`\n\n   - config_item()\n\n - :doc:`Config Class <../libraries/config>`\n\n   - config->item()\n   - config->slash_item()\n\n - :doc:`Input Class <../libraries/input>`\n\n   - input->get()\n   - input->post()\n   - input->get_post()\n   - input->cookie()\n   - input->server()\n   - input->input_stream()\n   - input->get_request_header()\n\n - :doc:`Session Class <../libraries/sessions>`\n\n   - session->userdata()\n   - session->flashdata()\n\n - :doc:`URI Class <../libraries/uri>`\n\n   - uri->segment()\n   - uri->rsegment()\n\n - :doc:`Array Helper <../helpers/array_helper>`\n\n   - element()\n   - elements()\n\n*******************************\nStep 11: Usage of XSS filtering\n*******************************\n\nMany functions in CodeIgniter allow you to use its XSS filtering feature\non demand by passing a boolean parameter. The default value of that\nparameter used to be boolean FALSE, but it is now changed to NULL and it\nwill be dynamically determined by your ``$config['global_xss_filtering']``\nvalue.\n\nIf you used to manually pass a boolean value for the ``$xss_filter``\nparameter or if you've always had ``$config['global_xss_filtering']`` set\nto FALSE, then this change doesn't concern you.\n\nOtherwise however, please review your usage of the following functions:\n\n - :doc:`Input Library <../libraries/input>`\n\n   - input->get()\n   - input->post()\n   - input->get_post()\n   - input->cookie()\n   - input->server()\n   - input->input_stream()\n\n - :doc:`Cookie Helper <../helpers/cookie_helper>` :php:func:`get_cookie()`\n\n.. important:: Another related change is that the ``$_GET``, ``$_POST``,\n\t``$_COOKIE`` and ``$_SERVER`` superglobals are no longer\n\tautomatically overwritten when global XSS filtering is turned on.\n\n*************************************************\nStep 12: Check for potential XSS issues with URIs\n*************************************************\n\nThe :doc:`URI Library <../libraries/uri>` used to automatically convert\na certain set of \"programmatic characters\" to HTML entities when they\nare encountered in a URI segment.\n\nThis was aimed at providing some automatic XSS protection, in addition\nto the ``$config['permitted_uri_chars']`` setting, but has proven to be\nproblematic and is now removed in CodeIgniter 3.0.\n\nIf your application has relied on this feature, you should update it to\nfilter URI segments through ``$this->security->xss_clean()`` whenever you\noutput them.\n\n****************************************************************\nStep 13: Check for usage of the 'xss_clean' Form validation rule\n****************************************************************\n\nA largely unknown rule about XSS cleaning is that it should *only be\napplied to output*, as opposed to input data.\n\nWe've made that mistake ourselves with our automatic and global XSS cleaning\nfeature (see previous step about XSS above), so now in an effort to discourage that\npractice, we're also removing 'xss_clean' from the officially supported\nlist of :doc:`form validation <../libraries/form_validation>` rules.\n\nBecause the :doc:`Form Validation library <../libraries/form_validation>`\ngenerally validates *input* data, the 'xss_clean' rule simply doesn't\nbelong in it.\n\nIf you really, really need to apply that rule, you should now also load the\n:doc:`Security Helper <../helpers/security_helper>`, which contains\n``xss_clean()`` as a regular function and therefore can be also used as\na validation rule.\n\n********************************************************\nStep 14: Update usage of Input Class's get_post() method\n********************************************************\n\nPreviously, the :doc:`Input Class <../libraries/input>` method ``get_post()``\nwas searching first in POST data, then in GET data. This method has been\nmodified so that it searches in GET then in POST, as its name suggests.\n\nA method has been added, ``post_get()``, which searches in POST then in GET, as\n``get_post()`` was doing before.\n\n********************************************************************\nStep 15: Update usage of Directory Helper's directory_map() function\n********************************************************************\n\nIn the resulting array, directories now end with a trailing directory\nseparator (i.e. a slash, usually).\n\n*************************************************************\nStep 16: Update usage of Database Forge's drop_table() method\n*************************************************************\n\nUp until now, ``drop_table()`` added an IF EXISTS clause by default or it didn't work\nat all with some drivers. In CodeIgniter 3.0, the IF EXISTS condition is no longer added\nby default and has an optional second parameter that allows that instead and is set to\nFALSE by default.\n\nIf your application relies on IF EXISTS, you'll have to change its usage.\n\n::\n\n\t// Now produces just DROP TABLE `table_name`\n\t$this->dbforge->drop_table('table_name');\n\n\t// Produces DROP TABLE IF EXISTS `table_name`\n\t$this->dbforge->drop_table('table_name', TRUE);\n\n.. note:: The given example uses MySQL-specific syntax, but it should work across\n\tall drivers with the exception of ODBC.\n\n***********************************************************\nStep 17: Change usage of Email library with multiple emails\n***********************************************************\n\nThe :doc:`Email Library <../libraries/email>` will automatically clear the\nset parameters after successfully sending emails. To override this behaviour,\npass FALSE as the first parameter in the ``send()`` method:\n\n::\n\n\tif ($this->email->send(FALSE))\n \t{\n \t\t// Parameters won't be cleared\n \t}\n\n***************************************************\nStep 18: Update your Form_validation language lines\n***************************************************\n\nTwo improvements have been made to the :doc:`Form Validation Library\n<../libraries/form_validation>`'s :doc:`language <../libraries/language>`\nfiles and error messages format:\n\n - :doc:`Language Library <../libraries/language>` line keys now must be\n   prefixed with **form_validation_** in order to avoid collisions::\n\n\t// Old\n\t$lang['rule'] = ...\n\n\t// New\n\t$lang['form_validation_rule'] = ...\n\n - The error messages format has been changed to use named parameters, to\n   allow more flexibility than what `sprintf()` offers::\n\n\t// Old\n\t'The %s field does not match the %s field.'\n\n\t// New\n\t'The {field} field does not match the {param} field.'\n\n.. note:: The old formatting still works, but the non-prefixed line keys\n\tare DEPRECATED and scheduled for removal in CodeIgniter 3.1+.\n\tTherefore you're encouraged to update its usage sooner rather than\n\tlater.\n\n************************************************************\nStep 19: Make sure your 'base_url' config value is not empty\n************************************************************\n\nWhen ``$config['base_url']`` is not set, CodeIgniter tries to automatically\ndetect what your website's base URL is. This is done purely for convenience\nwhen you are starting development of a new application.\n\nAuto-detection is never reliable and also has security implications, which\nis why you should **always** have it manually configured!\n\nOne of the changes in CodeIgniter 3.0.3 is how this auto-detection works,\nand more specifically it now falls back to the server's IP address instead\nof the hostname requested by the client. Therefore, if you've ever relied\non auto-detection, it will change how your website works now.\n\nIn case you need to allow e.g. multiple domains, or both http:// and\nhttps:// prefixes to be dynamically used depending on the request,\nremember that *application/config/config.php* is still a PHP script, in\nwhich you can create this logic with a few lines of code. For example::\n\n\t$allowed_domains = array('domain1.tld', 'domain2.tld');\n\t$default_domain  = 'domain1.tld';\n\n\tif (in_array($_SERVER['HTTP_HOST'], $allowed_domains, TRUE))\n\t{\n\t\t$domain = $_SERVER['HTTP_HOST'];\n\t}\n\telse\n\t{\n\t\t$domain = $default_domain;\n\t}\n\n\tif ( ! empty($_SERVER['HTTPS']))\n\t{\n\t\t$config['base_url'] = 'https://'.$domain;\n\t}\n\telse\n\t{\n\t\t$config['base_url'] = 'http://'.$domain;\n\t}\n\n\n****************************************************************\nStep 20: Remove usage of (previously) deprecated functionalities\n****************************************************************\n\nIn addition to the ``$autoload['core']`` configuration setting, there's a\nnumber of other functionalities that have been removed in CodeIgniter 3.0.0:\n\nThe SHA1 library\n================\n\nThe previously deprecated SHA1 library has been removed, alter your code to use PHP's native\n``sha1()`` function to generate a SHA1 hash.\n\nAdditionally, the ``sha1()`` method in the **Encrypt Library** has been removed.\n\nThe EXT constant\n================\n\nUsage of the ``EXT`` constant has been deprecated since dropping support for PHP 4. There's no\nlonger a need to maintain different filename extensions and in this new CodeIgniter version,\nthe ``EXT`` constant has been removed. Use just '.php' instead.\n\nSmiley helper\n=============\n\nThe *Smiley Helper* is a legacy feature from EllisLab's ExpressionEngine product.\nHowever, it is too specific for a general purpose framework like CodeIgniter\nand as such it is now deprecated.\n\nAlso, the previously deprecated ``js_insert_smiley()`` (since version 1.7.2) is now removed.\n\nThe Encrypt library\n===================\n\nFollowing numerous vulnerability reports, the **Encrypt Library** has been deprecated and a\nnew, :doc:`Encryption Library <../libraries/encryption>` is added to take its place.\n\nThe new library requires either the `MCrypt extension <https://secure.php.net/mcrypt>`_ (and /dev/urandom\navailability) or PHP 5.3.3 and the `OpenSSL extension <https://secure.php.net/openssl>`_.\nWhile this might be rather inconvenient, it is a requirement that allows us to have properly\nimplemented cryptographic functions.\n\n.. note:: The **Encrypt Library** is still available for the purpose of keeping\n\tbackwards compatibility.\n\n.. important:: You are strongly encouraged to switch to the new :doc:`Encryption Library\n\t<../libraries/encryption>` as soon as possible!\n\nThe Cart library\n================\n\nThe *Cart Library*, similarly to the *Smiley Helper* is too specific for\nCodeIgniter. It is now deprecated and scheduled for removal in\nCodeIgniter 3.1+.\n\n.. note:: The library is still available, but you're strongly encouraged to\n\tremove its usage sooner rather than later.\n\nDatabase drivers 'mysql', 'sqlite', 'mssql', 'pdo/dblib'\n========================================================\n\nThe **mysql** driver utilizes the old 'mysql' PHP extension, known for its aging code base and\nmany low-level problems. The extension is deprecated as of PHP 5.5 and CodeIgniter deprecates\nit in version 3.0, switching the default configured MySQL driver to **mysqli**.\n\nPlease use either the 'mysqli' or 'pdo/mysql' drivers for MySQL. The old 'mysql' driver will be\nremoved at some point in the future.\n\nThe **sqlite**, **mssql** and **pdo/dblib** (also known as pdo/mssql or pdo/sybase) drivers\nall depend on PHP extensions that for different reasons no longer exist since PHP 5.3.\n\nTherefore we are now deprecating these drivers as we will have to remove them in one of the next\nCodeIgniter versions. You should use the more advanced, **sqlite3**, **sqlsrv** or **pdo/sqlsrv**\ndrivers respectively.\n\n.. note:: These drivers are still available, but you're strongly encouraged to switch to other ones\n\tsooner rather than later.\n\nSecurity helper do_hash()\n=========================\n\n:doc:`Security Helper <../helpers/security_helper>` function ``do_hash()`` is now just an alias for\nPHP's native ``hash()`` function. It is deprecated and scheduled for removal in CodeIgniter 3.1+.\n\n.. note:: This function is still available, but you're strongly encouraged to remove its usage sooner\n\trather than later.\n\nThe $config['global_xss_filtering'] setting\n===========================================\n\nAs already explained above, XSS filtering should not be done on input data,\nbut on output instead. Therefore, the ``$config['global_xss_filtering']``,\nwhich automatically filters *input* data, is considered a bad practice and\nis now deprecated.\n\nInstead, you should manually escape any user-provided data via the\n:php:func:`xss_clean()` function when you need to output it, or use a\nlibrary like `HTML Purifier <http://htmlpurifier.org/>`_ that does that\nfor you.\n\n.. note:: The setting is still available, but you're strongly encouraged to\n\tremove its usage sooner rather than later.\n\nFile helper read_file()\n=======================\n\n:doc:`File Helper <../helpers/file_helper>` function ``read_file()`` is now just an alias for\nPHP's native ``file_get_contents()`` function. It is deprecated and scheduled for removal in\nCodeIgniter 3.1+.\n\n.. note:: This function is still available, but you're strongly encouraged to remove its usage sooner\n\trather than later.\n\nString helper repeater()\n========================\n\n:doc:`String Helper <../helpers/string_helper>` function ``repeater()`` is now just an alias for\nPHP's native ``str_repeat()`` function. It is deprecated and scheduled for removal in CodeIgniter 3.1+.\n\n.. note:: This function is still available, but you're strongly encouraged to remove its usage sooner\n\trather than later.\n\nString helper trim_slashes()\n============================\n\n:doc:`String Helper <../helpers/string_helper>` function ``trim_slashes()`` is now just an alias\nfor PHP's native ``trim()`` function (with a slash passed as its second argument). It is deprecated and\nscheduled for removal in CodeIgniter 3.1+.\n\n.. note:: This function is still available, but you're strongly encouraged to remove its usage sooner\n\trather than later.\n\nForm helper form_prep()\n=======================\n\n:doc:`Form Helper <../helpers/form_helper>` function ``form_prep()`` is now\njust an alias for :doc:`common function </general/common_functions>`\n:func:`html_escape()`. It is deprecated and will be removed in the future.\n\nPlease use :php:func:`html_escape()` instead.\n\n.. note:: This function is still available, but you're strongly encouraged\n\tto remove its usage sooner rather than later.\n\nEmail helper functions\n======================\n\nThe *Email Helper* only has two functions:\n\n - ``valid_email()``\n - ``send_email()``\n\nBoth of them are now aliases for PHP's native ``filter_var()`` and ``mail()`` functions, respectively.\nTherefore, the *Email Helper* altogether is being deprecated and is scheduled for removal in CodeIgniter 3.1+.\n\n.. note:: These functions are still available, but you're strongly encouraged to remove their usage\n\tsooner rather than later.\n\nDate helper standard_date()\n===========================\n\n:doc:`Date Helper <../helpers/date_helper>` function ``standard_date()`` is being deprecated due\nto the availability of native PHP `constants <https://secure.php.net/manual/en/class.datetime.php#datetime.constants.types>`_,\nwhich when combined with ``date()`` provide the same functionality. Furthermore, they have the\nexact same names as the ones supported by ``standard_date()``. Here are examples of how to replace\nits usage:\n\n::\n\n\t// Old way\n\tstandard_date(); // defaults to standard_date('DATE_RFC822', now());\n\n\t// Replacement\n\tdate(DATE_RFC822, now());\n\n\t// Old way\n\tstandard_date('DATE_ATOM', $time);\n\n\t// Replacement\n\tdate(DATE_ATOM, $time);\n\n.. note:: This function is still available, but you're strongly encouraged to remove its usage sooner\n\trather than later as it is scheduled for removal in CodeIgniter 3.1+.\n\nHTML helpers nbs(), br()\n========================\n\n:doc:`HTML Helper <../helpers/html_helper>` functions ``nbs()`` and ``br()`` are just aliases\nfor the native ``str_repeat()`` function used with ``&nbsp;`` and ``<br >`` respectively.\n\nBecause there's no point in just aliasing native PHP functions, they are now deprecated and\nscheduled for removal in CodeIgniter 3.1+.\n\n.. note:: These functions are still available, but you're strongly encouraged to remove their usage\n\tsooner rather than later.\n\nPagination library 'anchor_class' setting\n=========================================\n\nThe :doc:`Pagination Library <../libraries/pagination>` now supports adding pretty much any HTML\nattribute to your anchors via the 'attributes' configuration setting. This includes passing the\n'class' attribute and using the separate 'anchor_class' setting no longer makes sense.\nAs a result of that, the 'anchor_class' setting is now deprecated and scheduled for removal in\nCodeIgniter 3.1+.\n\n.. note:: This setting is still available, but you're strongly encouraged to remove its usage sooner\n\trather than later.\n\nString helper random_string() types 'unique' and 'encrypt'\n==========================================================\n\nWhen using the :doc:`String Helper <../helpers/string_helper>` function :php:func:`random_string()`,\nyou should no longer pass the **unique** and **encrypt** randomization types. They are only\naliases for **md5** and **sha1** respectively and are now deprecated and scheduled for removal\nin CodeIgniter 3.1+.\n\n.. note:: These options are still available, but you're strongly encouraged to remove their usage\n\tsooner rather than later.\n\nURL helper url_title() separators 'dash' and 'underscore'\n=========================================================\n\nWhen using the :doc:`URL Helper <../helpers/url_helper>` function :php:func:`url_title()`, you\nshould no longer pass **dash** or **underscore** as the word separator. This function will\nnow accept any character and you should just pass the chosen character directly, so you\nshould write '-' instead of 'dash' and '_' instead of 'underscore'.\n\n**dash** and **underscore** now act as aliases and are deprecated and scheduled for removal\nin CodeIgniter 3.1+.\n\n.. note:: These options are still available, but you're strongly encouraged to remove their usage\n\tsooner rather than later.\n\nSession Library method all_userdata()\n=====================================\n\nAs seen in the :doc:`Change Log <../changelog>`, :doc:`Session Library <../libraries/sessions>`\nmethod ``userdata()`` now allows you to fetch all userdata by simply omitting its parameter::\n\n\t$this->session->userdata();\n\nThis makes the ``all_userdata()`` method redudant and therefore it is now just an alias for\n``userdata()`` with the above shown usage and is being deprecated and scheduled for removal\nin CodeIgniter 3.1+.\n\n.. note:: This method is still available, but you're strongly encouraged to remove its usage\n\tsooner rather than later.\n\nDatabase Forge method add_column() with an AFTER clause\n=======================================================\n\nIf you have used the **third parameter** for :doc:`Database Forge <../database/forge>` method\n``add_column()`` to add a field for an AFTER clause, then you should change its usage.\n\nThat third parameter has been deprecated and scheduled for removal in CodeIgniter 3.1+.\n\nYou should now put AFTER clause field names in the field definition array instead::\n\n\t// Old usage:\n\t$field = array(\n\t\t'new_field' => array('type' => 'TEXT')\n\t);\n\n\t$this->dbforge->add_column('table_name', $field, 'another_field');\n\n\t// New usage:\n\t$field = array(\n\t\t'new_field' => array('type' => 'TEXT', 'after' => 'another_field')\n\t);\n\n\t$this->dbforge->add_column('table_name', $field);\n\n.. note:: The parameter is still available, but you're strongly encouraged to remove its usage\n\tsooner rather than later.\n\n.. note:: This is for MySQL and CUBRID databases only! Other drivers don't support this\n\tclause and will silently ignore it.\n\nURI Routing methods fetch_directory(), fetch_class(), fetch_method()\n====================================================================\n\nWith properties ``CI_Router::$directory``, ``CI_Router::$class`` and ``CI_Router::$method``\nbeing public and their respective ``fetch_*()`` no longer doing anything else to just return\nthe properties - it doesn't make sense to keep them.\n\nThose are all internal, undocumented methods, but we've opted to deprecate them for now\nin order to maintain backwards-compatibility just in case. If some of you have utilized them,\nthen you can now just access the properties instead::\n\n\t$this->router->directory;\n\t$this->router->class;\n\t$this->router->method;\n\n.. note:: Those methods are still available, but you're strongly encouraged to remove their usage\n\tsooner rather than later.\n\nInput library method is_cli_request()\n=====================================\n\nCalls to the ``CI_Input::is_cli_request()`` method are necessary at many places\nin the CodeIgniter internals and this is often before the :doc:`Input Library\n<../libraries/input>` is loaded. Because of that, it is being replaced by a common\nfunction named :php:func:`is_cli()` and this method is now just an alias.\n\nThe new function is both available at all times for you to use and shorter to type.\n\n::\n\n\t// Old\n\t$this->input->is_cli_request();\n\n\t// New\n\tis_cli();\n\n``CI_Input::is_cli_request()`` is now now deprecated and scheduled for removal in\nCodeIgniter 3.1+.\n\n.. note:: This method is still available, but you're strongly encouraged to remove its usage\n\tsooner rather than later.\n\nConfig library method system_url()\n==================================\n\nUsage of ``CI_Config::system_url()`` encourages insecure coding practices.\nNamely, your CodeIgniter *system/* directory shouldn't be publicly accessible\nfrom a security point of view.\n\nBecause of this, this method is now deprecated and scheduled for removal in\nCodeIgniter 3.1+.\n\n.. note:: This method is still available, but you're strongly encouraged to remove its usage\n\tsooner rather than later.\n\nThe Javascript library\n======================\n\nThe *Javascript Library* has always had an 'experimental' status and was\nnever really useful, nor a proper solution.\n\nIt is now deprecated and scheduled for removal in CodeIgniter 3.1+.\n\n.. note:: This library is still available, but you're strongly encouraged to remove its usage\n\tsooner rather than later.\n\nForm Validation method prep_for_form()\n======================================\n\nThe :doc:`Form Validation Library <../libraries/form_validation>` has a\n``prep_for_form()`` method, which is/can also be used as a rule in\n``set_rules()`` to automatically perform HTML encoding on input data.\n\nAutomatically encoding input (instead of output) data is a bad practice in\nthe first place, and CodeIgniter and PHP itself offer other alternatives\nto this method anyway.\nFor example, :doc:`Form Helper <../helpers/form_helper>` functions will\nautomatically perform HTML escaping when necessary.\n\nTherefore, the *prep_for_form* method/rule is pretty much useless and is now\ndeprecated and scheduled for removal in 3.1+.\n\n.. note:: The method is still available, but you're strongly encouraged to\n\tremove its usage sooner rather than later.\n\n***********************************************************\nStep 21: Check your usage of Text helper highlight_phrase()\n***********************************************************\n\nThe default HTML tag used by :doc:`Text Helper <../helpers/text_helper>` function\n:func:`highlight_phrase()` has been changed from ``<strong>`` to the new HTML5\ntag ``<mark>``.\n\nUnless you've used your own highlighting tags, this might cause trouble\nfor your visitors who use older web browsers such as Internet Explorer 8.\nWe therefore suggest that you add the following code to your CSS files\nin order to avoid backwards compatibility with old browsers::\n\n\tmark {\n\t\tbackground: #ff0;\n\t\tcolor: #000;\n\t};\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_301.rst",
    "content": "#############################\nUpgrading from 3.0.0 to 3.0.1\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your *system/* directory.\n\n.. note:: If you have any custom developed files in these directories,\n\tplease make copies of them first.\n\nStep 2: Update your CLI error templates\n=======================================\n\nReplace all files under your *application/views/errors/cli/* directory.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_302.rst",
    "content": "#############################\nUpgrading from 3.0.1 to 3.0.2\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your *system/* directory.\n\n.. note:: If you have any custom developed files in these directories,\n\tplease make copies of them first.\n\nStep 2: Update your application/config/constants.php file\n=========================================================\n\nThe *application/config/constants.php* file has been updated to check\nif constants aren't already defined before doing that, making it easier\nto add an environment-specific configuration.\n\n.. note:: If you've made modifications to this file, please make a\n\tbackup first and cross-check the differences first."
  },
  {
    "path": "user_guide_src/source/installation/upgrade_303.rst",
    "content": "#############################\nUpgrading from 3.0.2 to 3.0.3\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your *system/* directory.\n\n.. note:: If you have any custom developed files in these directories,\n\tplease make copies of them first.\n\nStep 2: Make sure your 'base_url' config value is not empty\n===========================================================\n\nWhen ``$config['base_url']`` is not set, CodeIgniter tries to automatically\ndetect what your website's base URL is. This is done purely for convenience\nwhen you are starting development of a new application.\n\nAuto-detection is never reliable and also has security implications, which\nis why you should **always** have it manually configured!\n\nOne of the changes in CodeIgniter 3.0.3 is how this auto-detection works,\nand more specifically it now falls back to the server's IP address instead\nof the hostname requested by the client. Therefore, if you've ever relied\non auto-detection, it will change how your website works now.\n\nIn case you need to allow e.g. multiple domains, or both http:// and\nhttps:// prefixes to be dynamically used depending on the request,\nremember that *application/config/config.php* is still a PHP script, in\nwhich you can create this logic with a few lines of code. For example::\n\n\t$allowed_domains = array('domain1.tld', 'domain2.tld');\n\t$default_domain  = 'domain1.tld';\n\n\tif (in_array($_SERVER['HTTP_HOST'], $allowed_domains, TRUE))\n\t{\n\t\t$domain = $_SERVER['HTTP_HOST'];\n\t}\n\telse\n\t{\n\t\t$domain = $default_domain;\n\t}\n\n\tif ( ! empty($_SERVER['HTTPS']))\n\t{\n\t\t$config['base_url'] = 'https://'.$domain;\n\t}\n\telse\n\t{\n\t\t$config['base_url'] = 'http://'.$domain;\n\t}\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_304.rst",
    "content": "#############################\nUpgrading from 3.0.3 to 3.0.4\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your *system/* directory.\n\n.. note:: If you have any custom developed files in these directories,\n\tplease make copies of them first.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_305.rst",
    "content": "#############################\nUpgrading from 3.0.4 to 3.0.5\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your *system/* directory.\n\n.. note:: If you have any custom developed files in these directories,\n\tplease make copies of them first.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_306.rst",
    "content": "#############################\nUpgrading from 3.0.5 to 3.0.6\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your *system/* directory.\n\n.. note:: If you have any custom developed files in these directories,\n\tplease make copies of them first.\n\nStep 2: Update your index.php file (optional)\n=============================================\n\nWe've made some tweaks to the index.php file, mostly related to proper\nusage of directory separators (i.e. use the ``DIRECTORY_SEPARATOR``\nconstant instead of a hard coded forward slash \"/\").\n\nNothing will break if you skip this step, but if you're running Windows\nor just want to be up to date with every change - we do recommend that\nyou update your index.php file.\n\n*Tip: Just copy the ``ENVIRONMENT``, ``$system_path``, ``$application_folder``\nand ``$view_folder`` declarations from the old file and put them into the\nnew one, replacing the defaults.*\n\nStep 3: Remove 'prep_for_form' usage (deprecation)\n==================================================\n\nThe :doc:`Form Validation Library <../libraries/form_validation>` has a\n``prep_for_form()`` method, which is/can also be used as a rule in\n``set_rules()`` to automatically perform HTML encoding on input data.\n\nAutomatically encoding input (instead of output) data is a bad practice in\nthe first place, and CodeIgniter and PHP itself offer other alternatives\nto this method anyway.\nFor example, :doc:`Form Helper <../helpers/form_helper>` functions will\nautomatically perform HTML escaping when necessary.\n\nTherefore, the *prep_for_form* method/rule is pretty much useless and is now\ndeprecated and scheduled for removal in 3.1+.\n\n.. note:: The method is still available, but you're strongly encouraged to\n\tremove its usage sooner rather than later.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_310.rst",
    "content": "#############################\nUpgrading from 3.0.6 to 3.1.0\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your *system/* directory.\n\n.. note:: If you have any custom developed files in these directories,\n\tplease make copies of them first.\n\nStep 2: Check your PHP version\n==============================\n\nWe recommend always running versions that are `currently supported\n<https://secure.php.net/supported-versions.php>`_, which right now is at least PHP 5.6.\n\nPHP 5.2.x versions are now officially not supported by CodeIgniter, and while 5.3.7+\nmay be at least runnable, we strongly discourage you from using any PHP versions below\nthe ones listed on the `PHP.net Supported Versions <https://secure.php.net/supported-versions.php>`_\npage.\n\nStep 3: If you're using the 'odbc' database driver, check for usage of Query Builder\n====================================================================================\n\n:doc:`Query Builder <../database/query_builder>` functionality and ``escape()`` can\nno longer be used with the 'odbc' database driver.\n\nThis is because, due to its nature, the `ODBC extension for PHP <https://secure.php.net/odbc>`_\ndoes not provide a function that allows to safely escape user-supplied strings for usage\ninside an SQL query (which our :doc:`Query Builder <../database/query_builder>` relies on).\n\nThus, user inputs MUST be bound, as shown in :doc:`Running Queries <../database/queries>`,\nunder the \"Query Bindings\" section.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_311.rst",
    "content": "#############################\nUpgrading from 3.1.0 to 3.1.1\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your *system/* directory.\n\n.. note:: If you have any custom developed files in these directories,\n\tplease make copies of them first.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_3110.rst",
    "content": "##############################\nUpgrading from 3.1.9 to 3.1.10\n##############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your *system/* directory.\n\n.. note:: If you have any custom developed files in these directories,\n\tplease make copies of them first.\n\nStep 2: Check for calls to is_countable()\n==========================================\n\n\nPHP 7.3 introduces a native `is_countable() <https://secure.php.net/is_countable>`_\nfunction, which creates a name collision with the ``is_countable()`` function\nwe've had in our :doc:`Inflector Helpers <../helpers/inflector_helper>`.\n\nIf you've been using the helper function in question, you should now rename\nthe calls to it to :php:func:`word_is_countable()`.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_3111.rst",
    "content": "###############################\nUpgrading from 3.1.10 to 3.1.11\n###############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your *system/* directory.\n\n.. note:: If you have any custom developed files in these directories,\n\tplease make copies of them first.\n\nStep 2: Replace config/mimes.php\n================================\n\nThis config file has received some updates. Please copy it to\n*application/config/mimes.php*.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_3112.rst",
    "content": "###############################\nUpgrading from 3.1.11 to 3.1.12\n###############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your *system/* directory.\n\n.. note:: If you have any custom developed files in these directories,\n\tplease make copies of them first.\n\nStep 2: Replace config/user_agents.php\n================================\n\nThis config file has received some updates. Please copy it to\n*application/config/user_agents.php*.\n\nStep 3: Replace config/mimes.php\n================================\n\nThis config file has received some updates. Please copy it to\n*application/config/mimes.php*.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_3113.rst",
    "content": "##############################\nUpgrading from 3.1.12 to 3.1.13\n##############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your *system/* directory.\n\n.. note:: If you have any custom developed files in these directories,\n\tplease make copies of them first.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_3114.rst",
    "content": "##############################\nUpgrading from 3.1.13 to 3.1.14\n##############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your *system/* directory.\n\n.. note:: If you have any custom developed files in these directories,\n\tplease make copies of them first.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_312.rst",
    "content": "#############################\nUpgrading from 3.1.1 to 3.1.2\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your *system/* directory.\n\n.. note:: If you have any custom developed files in these directories,\n\tplease make copies of them first.\n\nStep 2: Update your \"ci_sessions\" database table\n================================================\n\nIf you're using the :doc:`Session Library </libraries/sessions>` with the\n'database' driver, you may have to ``ALTER`` your sessions table for your\nsessions to continue to work.\n\n.. note:: The table in question is not necessarily named \"ci_sessions\".\n\tIt is what you've set as your ``$config['sess_save_path']``.\n\nThis will only affect you if you've changed your ``session.hash_function``\n*php.ini* setting to something like 'sha512'. Or if you've been running\nan older CodeIgniter version on PHP 7.1+.\n\nIt is recommended that you do this anyway, just to avoid potential issues\nin the future if you do change your configuration.\n\nJust execute the one of the following SQL queries, depending on your\ndatabase::\n\n\t// MySQL:\n\tALTER TABLE ci_sessions CHANGE id id varchar(128) NOT NULL;\n\n\t// PostgreSQL\n\tALTER TABLE ci_sessions ALTER COLUMN id SET DATA TYPE varchar(128);\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_313.rst",
    "content": "#############################\nUpgrading from 3.1.2 to 3.1.3\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your *system/* directory.\n\n.. note:: If you have any custom developed files in these directories,\n\tplease make copies of them first.\n\nStep 2: Remove usage of nice_date() helper (deprecation)\n========================================================\n\nThe :doc:`Date Helper <../helpers/date_helper>` function ``nice_date()`` is\nno longer useful since the introduction of PHP's `DateTime classes\n<https://secure.php.net/datetime>`_\n\nYou can replace it with the following:\n::\n\n\tDateTime::createFromFormat($input_format, $input_date)->format($desired_output_format);\n\nThus, ``nice_date()`` is now deprecated and scheduled for removal in\nCodeIgniter 3.2+.\n\n.. note:: The function is still available, but you're strongly encouraged\n\tto remove its usage sooner rather than later.\n\nStep 3: Remove usage of $config['standardize_newlines']\n=======================================================\n\nThe :doc:`Input Library <../libraries/input>` would optionally replace\noccurrences of `\\r\\n`, `\\r`, `\\n` in input data with whatever the ``PHP_EOL``\nvalue is on your system - if you've set ``$config['standardize_newlines']``\nto ``TRUE`` in your *application/config/config.php*.\n\nThis functionality is now deprecated and scheduled for removal in\nCodeIgniter 3.2.+.\n\n.. note:: The functionality is still available, but you're strongly\n\tencouraged to remove its usage sooner rather than later.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_314.rst",
    "content": "#############################\nUpgrading from 3.1.3 to 3.1.4\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your *system/* directory.\n\n.. note:: If you have any custom developed files in these directories,\n\tplease make copies of them first.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_315.rst",
    "content": "#############################\nUpgrading from 3.1.4 to 3.1.5\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your *system/* directory.\n\n.. note:: If you have any custom developed files in these directories,\n\tplease make copies of them first.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_316.rst",
    "content": "#############################\nUpgrading from 3.1.5 to 3.1.6\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your *system/* directory.\n\n.. note:: If you have any custom developed files in these directories,\n\tplease make copies of them first.\n\nStep 2: Remove usage of the APC Cache driver (deprecation)\n==========================================================\n\nThe :doc:`Cache Library <../libraries/caching>` APC driver is now\ndeprecated, as the APC extension is effectively dead, as explained in its\n`PHP Manual page <https://secure.php.net/manual/en/intro.apc.php>`_.\n\nIf your application happens to be using it, you can switch to another\ncache driver, as APC support will be removed in a future CodeIgniter\nversion.\n\n.. note:: The driver is still available, but you're strongly encouraged\n\tto remove its usage sooner rather than later.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_317.rst",
    "content": "#############################\nUpgrading from 3.1.6 to 3.1.7\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your *system/* directory.\n\n.. note:: If you have any custom developed files in these directories,\n\tplease make copies of them first.\n\nStep 2: Remove usage of CAPTCHA helper extra parameters (deprecation)\n=====================================================================\n\nThe :doc:`CAPTCHA Helper <../helpers/captcha_helper>` function\n:php:func:`create_captcha()` allows passing of its ``img_path``, ``img_url``\nand ``font_path`` options as the 2nd, 3rd and 4th parameters respectively.\n\nThis kind of usage is now deprecated and you should just pass the options\nin question as part of the first parameter array.\n\n.. note:: The functionality in question is still available, but you're\n\tstrongly encouraged to remove its usage sooner rather than later.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_318.rst",
    "content": "#############################\nUpgrading from 3.1.7 to 3.1.8\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your *system/* directory.\n\n.. note:: If you have any custom developed files in these directories,\n\tplease make copies of them first.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_319.rst",
    "content": "#############################\nUpgrading from 3.1.8 to 3.1.9\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your *system/* directory.\n\n.. note:: If you have any custom developed files in these directories,\n\tplease make copies of them first.\n\n\nStep 2: Replace config/mimes.php\n================================\n\nThis config file has received some updates. Please copy it to\n*application/config/mimes.php*.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_320.rst",
    "content": "#############################\nUpgrading from 3.1.x to 3.2.x\n#############################\n\nBefore performing an update you should take your site offline by\nreplacing the index.php file with a static one.\n\nStep 1: Update your CodeIgniter files\n=====================================\n\nReplace all files and directories in your *system/* directory.\n\n.. note:: If you have any custom developed files in these directories,\n\tplease make copies of them first.\n\nStep 2: Check your PHP version\n==============================\n\nWe recommend always running versions that are `currently supported\n<https://secure.php.net/supported-versions.php>`_, which right now is at least PHP 5.6.\n\nPHP 5.3.x versions are now officially not supported by CodeIgniter, and while 5.4.8+\nmay be at least runnable, we strongly discourage you from using any PHP versions below\nthe ones listed on the `PHP.net Supported Versions <https://secure.php.net/supported-versions.php>`_\npage.\n\nStep 3: Remove calls to ``CI_Model::__construct()``\n===================================================\n\nThe class constructor for ``CI_Model`` never contained vital code or useful\nlogic, only a single line to log a message. A change in CodeIgniter 3.1.7\nmoved this log message elsewhere and that naturally made the constructor\ncompletely unnecessary. However, it was left in place to avoid immedate BC\nbreaks in a minor release.\n\nIn version 3.2.0, that constructor is entirely removed, which would result\nin fatal errors on attempts to call it. Particularly in code like this:\n::\n\n\tclass Some_model extends CI_Model {\n\n\t\tpublic function __construct()\n\t\t{\n\t\t\tparent::__construct(); // calls CI_Model::__construct()\n\n\t\t\tdo_some_other_thing();\n\t\t}\n\t}\n\nAll you need to do is remove that ``parent::__construct()`` call. On a side\nnote, the following seems to be a very common practice:\n::\n\n\tclass Some_class extends CI_Something {\n\n\t\tpublic function __construct()\n\t\t{\n\t\t\tparent::__construct();\n\t\t}\n\t}\n\nPlease, do NOT do this! It's pointless; it serves no purpose and doesn't do\nanything. If a parent class has a ``__construct()`` method, it will be\ninherited by all its child classes and will execute just fine - you DON'T\nhave to explicitly call it unless you want to extend its logic.\n\nStep 4: Change database connection handling\n===========================================\n\n\"Loading\" a database, whether by using the *config/autoload.php* settings\nor manually via calling ``$this->load->database()`` or the less-known\n``DB()`` function, will now throw a ``RuntimeException`` in case of a\nfailure.\n\nIn addition, being unable to set the configured character set is now also\nconsidered a connection failure.\n\n.. note:: This has been the case for most database drivers in the in the\n\tpast as well (i.e. all but the 'mysql', 'mysqli' and 'postgre'\n\tdrivers).\n\nWhat this means is that if you're unable to connect to a database, or\nhave an erroneous character set configured, CodeIgniter will no longer\nfail silently, but will throw an exception instead.\n\nYou may choose to explicitly catch it (and for that purpose you can't use\n*config/autoload.php* to load the :doc:`Database Class <../database/index>`)\n::\n\n\ttry\n\t{\n\t\t$this->load->database();\n\t}\n\tcatch (RuntimeException $e)\n\t{\n\t\t// Handle the failure\n\t}\n\nOr you may leave it to CodeIgniter's default exception handler, which would\nlog the error message and display an error screen if you're running in\ndevelopment mode.\n\nRemove db_set_charset() calls\n-----------------------------\n\nWith the above-mentioned changes, the purpose of the ``db_set_charset()``\nmethod would now only be to change the connection character set at runtime.\nThat doesn't make sense and that's the reason why most database drivers\ndon't support it at all.\nThus, ``db_set_charset()`` is no longer necessary and is removed.\n\nStep 5: Check logic related to URI parsing of CLI requests\n==========================================================\n\nWhen running a CodeIgniter application from the CLI, the\n:doc:`URI Library <../libraries/uri>` will now ignore the\n``$config['url_suffix']`` and ``$config['permitted_uri_chars']``\nconfiguration settings.\n\nThese two options don't make sense under the command line (which is why\nthis change was made) and therefore you shouldn't be affected by this, but\nif you've relied on them for some reason, you'd probably have to make some\nchanges to your code.\n\nStep 6: Check Cache Library configurations for Redis, Memcache(d)\n=================================================================\n\nThe new improvements for the 'redis' and 'memcached' drivers of the\n:doc:`Cache Library <../libraries/caching>` may require some small\nadjustments to your configuration values ...\n\nRedis\n-----\n\nIf you're using the 'redis' driver with a UNIX socket connection, you'll\nhave to move the socket path from ``$config['socket']`` to\n``$config['host']`` instead.\n\nThe ``$config['socket_type']`` option is also removed, although that won't\naffect your application - it will be ignored and the connection type will\nbe determined by the format used for ``$config['host']`` instead.\n\nMemcache(d)\n-----------\n\nThe 'memcached' will now ignore configurations that don't specify a ``host``\nvalue (previously, it just set the host to the default '127.0.0.1').\n\nTherefore, if you've added a configuration that only sets e.g. a ``port``,\nyou will now have to explicitly set the ``host`` to '127.0.0.1' as well.\n\nStep 7: Check usage of the Email library\n========================================\n\nThe :doc:`Email Library <../libraries/email>` will now by default check the\nvalidity of all e-mail addresses passed to it. This check used to be Off by\ndefault, and required explicitly setting the **validate** option to ``TRUE``\nin order to enable it.\n\nNaturally, a validity check should not result in any problems, but this is\ntechnically a backwards-compatibility break and you should check that\neverything works fine.\nIf something indeed goes wrong with that, please report it as a bug to us,\nand you can disable the **validate** option to revert to the old behavior.\n\nStep 8: Check usage of doctype() HTML helper\n============================================\n\nThe :doc:`HTML Helper <../helpers/html_helper>` function\n:php:func:`doctype()` used to default to 'xhtml1-strict' (XHTML 1.0 Strict)\nwhen no document type was specified. That default value is now changed to\n'html5', which obviously stands for the modern HTML 5 standard.\n\nNothing should be really broken by this change, but if your application\nrelies on the default value, you should double-check it and either\nexplicitly set the desired format, or adapt your front-end to use proper\nHTML 5 formatting.\n\nStep 9: Check usage of form_upload() Form helper\n================================================\n\nThe :doc:`Form Helper <../helpers/form_helper>` function\n:php:func:`form_upload()` used to have 3 parameters, the second of which\n(``$value``) was never used, as it doesn't make sense for an HTML ``input``\ntag of the \"file\" type.\n\nThat dead parameter is now removed, and so if you've used the third one\n(``$extra``), having code like this::\n\n\tform_upload('name', 'irrelevant value', $extra);\n\nYou should change it to::\n\n\tform_upload('name', $extra);\n\nStep 10: Remove usage of previously deprecated functionalities\n==============================================================\n\nThe following is a list of functionalities deprecated in previous\nCodeIgniter versions that have been removed in 3.2.0:\n\n- ``$config['allow_get_array']`` (use ``$_GET = array();`` instead)\n- ``$config['standardize_newlines']``\n- ``$config['rewrite_short_tags']`` (no impact; irrelevant on PHP 5.4+)\n\n- 'sqlite' database driver (no longer shipped with PHP 5.4+; 'sqlite3' is still available)\n\n- ``CI_Input::is_cli_request()`` (use :php:func:`is_cli()` instead)\n- ``CI_Router::fetch_directory()`` (use ``CI_Router::$directory`` instead)\n- ``CI_Router::fetch_class()`` (use ``CI_Router::$class`` instead)\n- ``CI_Router::fetch_method()`` (use ``CI_Router::$method`` instead)\n- ``CI_Config::system_url()`` (encourages insecure practices)\n- ``CI_Form_validation::prep_for_form()`` (the *prep_for_form* rule)\n\n- ``standard_date()`` :doc:`Date Helper <../helpers/date_helper>` function (use ``date()`` instead)\n- ``nice_date()`` :doc:`Date Helper <../helpers/date_helper>` function (use ``DateTime::format()`` instead)\n- ``do_hash()`` :doc:`Security Helper <../helpers/security_helper>` function (use ``hash()`` instead)\n- ``br()`` :doc:`HTML Helper <../helpers/html_helper>` function (use ``str_repeat()`` with ``'<br />'`` instead)\n- ``nbs()`` :doc:`HTML Helper <../helpers/html_helper>` function (use ``str_repeat()`` with ``'&nbsp;'`` instead)\n- ``trim_slashes()`` :doc:`String Helper <../helpers/string_helper>` function (use ``trim()`` with ``'/'`` instead)\n- ``repeater()`` :doc:`String Helper <../helpers/string_helper>` function (use ``str_repeat()`` instead)\n- ``read_file()`` :doc:`File Helper <../helpers/file_helper>` function (use ``file_get_contents()`` instead)\n- ``form_prep()`` :doc:`Form Helper <../helpers/form_helper>` function (use :php:func:`html_escape()` instead)\n\n- The entire *Encrypt Library* (the newer :doc:`Encryption Library <../libraries/encryption>` is still available)\n- The entire *Cart Library* (an archived version is available on GitHub: `bcit-ci/ci3-cart-library <https://github.com/bcit-ci/ci3-cart-library>`_)\n- The entire *Javascript Library* (it was always experimental in the first place)\n\n- The entire *Email Helper*, which only had two functions:\n\n   - ``valid_email()`` (use ``filter_var($email, FILTER_VALIDATE_EMAIL)`` instead)\n   - ``send_email()`` (use ``mail()`` instead)\n\n- The entire *Smiley Helper* (an archived version is available on GitHub: `bcit-ci/ci3-smiley-helper <https://github.com/bcit-ci/ci3-smiley-helper>`_)\n\n- The ``$_after`` parameter from :doc:`Database Forge <../database/forge>` method ``add_column()``.\n- The ``anchor_class`` option from :doc:`Pagination Library <../libraries/pagination>` (use ``class`` instead).\n- The ``unique`` and ``encrypt`` options from :doc:`String Helper <../helpers/string_helper>` function ``random_string()``.\n- The ``underscore`` and ``dash`` options from :doc:`URL Helper <../helpers/url_helper>`` function :php:func:`url_title()`.\n- The ``$img_path``, ``$img_url`` and ``$font_path`` parameters from\n  :doc:`CAPCHA Helper <../helpers/captcha_helper>` function :php:func:`create_captcha()` (pass as array options instead).\n\nStep 11: Make sure you're validating all user inputs\n====================================================\n\nThe :doc:`Input Library <../libraries/input>` used to (often\nunconditionally) filter and/or sanitize user input in the ``$_GET``,\n``$_POST`` and ``$_COOKIE`` superglobals.\n\nThis was a legacy feature from older times, when things like\n`register_globals <https://secure.php.net/register_globals>`_ and\n`magic_quotes_gpc <https://secure.php.net/magic_quotes_gpc>`_ existed in\nPHP.\nIt was a necessity back then, but this is no longer the case and reliance\non global filters is a bad practice, giving you a false sense of security.\n\nThis functionality is now removed, and so if you've relied on it for\nwhatever reasons, you should double-check that you are properly validating\nall user inputs in your application (as you always should do).\n\nStep 12: Clear your output cache (optional)\n===========================================\n\nInternal changes to the :doc:`Output Class <../libraries/output>` make it\nso that if you're using the :doc:`Web Page Caching <../general/caching>`\nfeature, you'll be left with some old, garbage cache files.\n\nThat shouldn't be a problem, but you may want to clear them.\n\nStep 13: Remove usage of OCI8 get_cursor() and stored_procedure() methods\n=========================================================================\n\nThe OCI8 :doc:`Database <database/index>` driver no longer has these two\nmethods that were specific to it and not present in other database drivers.\nThe ``$curs_id`` property is also removed.\n\nIf you were using those, you can create your own cursors via ``oci_new_cursor()``\nand the publicly accessible ``$conn_id``.\n\nStop 14: Replace $config['log_file_extension'] with $config['log_filename'] in application/config/config.php\n============================================================================================================\n\nYou can now specify the full log filename via ``$config['log_filename']``.\nAdd this configuration option to your **application/config/config.php**,\nif you haven't copied the new one over.\n\nThe previously existing ``$config['log_file_extension']`` option has been\nremoved and no longer works. However, its functionality is essentially\nintegrated into the new ``$config['log_filename']``, since it includes the\nfilename extension in itself.\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrade_b11.rst",
    "content": "###################################\nUpgrading From Beta 1.0 to Beta 1.1\n###################################\n\nTo upgrade to Beta 1.1 please perform the following steps:\n\nStep 1: Replace your index file\n===============================\n\nReplace your main index.php file with the new index.php file. Note: If\nyou have renamed your \"system\" folder you will need to edit this info in\nthe new file.\n\nStep 2: Relocate your config folder\n===================================\n\nThis version of CodeIgniter now permits multiple sets of \"applications\"\nto all share a common set of backend files. In order to enable each\napplication to have its own configuration values, the config directory\nmust now reside inside of your application folder, so please move it\nthere.\n\nStep 3: Replace directories\n===========================\n\nReplace the following directories with the new versions:\n\n-  drivers\n-  helpers\n-  init\n-  libraries\n-  scaffolding\n\nStep 4: Add the calendar language file\n======================================\n\nThere is a new language file corresponding to the new calendaring class\nwhich must be added to your language folder. Add the following item to\nyour version: language/english/calendar_lang.php\n\nStep 5: Edit your config file\n=============================\n\nThe original application/config/config.php file has a typo in it Open\nthe file and look for the items related to cookies::\n\n\t$conf['cookie_prefix']\t= \"\";\n\t$conf['cookie_domain']\t= \"\";\n\t$conf['cookie_path']\t= \"/\";\n\nChange the array name from $conf to $config, like this::\n\n\t$config['cookie_prefix']\t= \"\";\n\t$config['cookie_domain']\t= \"\";\n\t$config['cookie_path']\t= \"/\";\n\nLastly, add the following new item to the config file (and edit the\noption if needed)::\n\n\t\n\t/*\n\t|------------------------------------------------\n\t| URI PROTOCOL\n\t|------------------------------------------------\n\t|\n\t| This item determines which server global \n\t| should be used to retrieve the URI string. The \n\t| default setting of \"auto\" works for most servers.\n\t| If your links do not seem to work, try one of \n\t| the other delicious flavors:\n\t| \n\t| 'auto'\t Default - auto detects\n\t| 'path_info'\t Uses the PATH_INFO \n\t| 'query_string'\tUses the QUERY_STRING\n\t*/\n\n\t$config['uri_protocol']\t= \"auto\";\n\n"
  },
  {
    "path": "user_guide_src/source/installation/upgrading.rst",
    "content": "#################################\nUpgrading From a Previous Version\n#################################\n\nPlease read the upgrade notes corresponding to the version you are\nupgrading from.\n\n.. toctree::\n\t:titlesonly:\n\n\tUpgrading from 3.1.12+ to 3.2.x <upgrade_320>\n\tUpgrading from 3.1.13 to 3.1.14 <upgrade_3114>\n\tUpgrading from 3.1.12 to 3.1.13 <upgrade_3113>\n\tUpgrading from 3.1.11 to 3.1.12 <upgrade_3112>\n\tUpgrading from 3.1.10 to 3.1.11 <upgrade_3111>\n\tUpgrading from 3.1.9 to 3.1.10 <upgrade_3110>\n\tUpgrading from 3.1.8 to 3.1.9 <upgrade_319>\n\tUpgrading from 3.1.7 to 3.1.8 <upgrade_318>\n\tUpgrading from 3.1.6 to 3.1.7 <upgrade_317>\n\tUpgrading from 3.1.5 to 3.1.6 <upgrade_316>\n\tUpgrading from 3.1.4 to 3.1.5 <upgrade_315>\n\tUpgrading from 3.1.3 to 3.1.4 <upgrade_314>\n\tUpgrading from 3.1.2 to 3.1.3 <upgrade_313>\n\tUpgrading from 3.1.1 to 3.1.2 <upgrade_312>\n\tUpgrading from 3.1.0 to 3.1.1 <upgrade_311>\n\tUpgrading from 3.0.6 to 3.1.0 <upgrade_310>\n\tUpgrading from 3.0.5 to 3.0.6 <upgrade_306>\n\tUpgrading from 3.0.4 to 3.0.5 <upgrade_305>\n\tUpgrading from 3.0.3 to 3.0.4 <upgrade_304>\n\tUpgrading from 3.0.2 to 3.0.3 <upgrade_303>\n\tUpgrading from 3.0.1 to 3.0.2 <upgrade_302>\n\tUpgrading from 3.0.0 to 3.0.1 <upgrade_301>\n\tUpgrading from 2.2.x to 3.0.x <upgrade_300>\n\tUpgrading from 2.2.2 to 2.2.3 <upgrade_223>\n\tUpgrading from 2.2.1 to 2.2.2 <upgrade_222>\n\tUpgrading from 2.2.0 to 2.2.1 <upgrade_221>\n\tUpgrading from 2.1.4 to 2.2.x <upgrade_220>\n\tUpgrading from 2.1.3 to 2.1.4 <upgrade_214>\n\tUpgrading from 2.1.2 to 2.1.3 <upgrade_213>\n\tUpgrading from 2.1.1 to 2.1.2 <upgrade_212>\n\tUpgrading from 2.1.0 to 2.1.1 <upgrade_211>\n\tUpgrading from 2.0.3 to 2.1.0 <upgrade_210>\n\tUpgrading from 2.0.2 to 2.0.3 <upgrade_203>\n\tUpgrading from 2.0.1 to 2.0.2 <upgrade_202>\n\tUpgrading from 2.0 to 2.0.1 <upgrade_201>\n\tUpgrading from 1.7.2 to 2.0 <upgrade_200>\n\tUpgrading from 1.7.1 to 1.7.2 <upgrade_172>\n\tUpgrading from 1.7.0 to 1.7.1 <upgrade_171>\n\tUpgrading from 1.6.3 to 1.7.0 <upgrade_170>\n\tUpgrading from 1.6.2 to 1.6.3 <upgrade_163>\n\tUpgrading from 1.6.1 to 1.6.2 <upgrade_162>\n\tUpgrading from 1.6.0 to 1.6.1 <upgrade_161>\n\tUpgrading from 1.5.4 to 1.6.0 <upgrade_160>\n\tUpgrading from 1.5.3 to 1.5.4 <upgrade_154>\n\tUpgrading from 1.5.2 to 1.5.3 <upgrade_153>\n\tUpgrading from 1.5.0 or 1.5.1 to 1.5.2 <upgrade_152>\n\tUpgrading from 1.4.1 to 1.5.0 <upgrade_150>\n\tUpgrading from 1.4.0 to 1.4.1 <upgrade_141>\n\tUpgrading from 1.3.3 to 1.4.0 <upgrade_140>\n\tUpgrading from 1.3.2 to 1.3.3 <upgrade_133>\n\tUpgrading from 1.3.1 to 1.3.2 <upgrade_132>\n\tUpgrading from 1.3 to 1.3.1 <upgrade_131>\n\tUpgrading from 1.2 to 1.3 <upgrade_130>\n\tUpgrading from 1.1 to 1.2 <upgrade_120>\n\tUpgrading from Beta 1.0 to Beta 1.1 <upgrade_b11>\n"
  },
  {
    "path": "user_guide_src/source/libraries/benchmark.rst",
    "content": "##################\nBenchmarking Class\n##################\n\nCodeIgniter has a Benchmarking class that is always active, enabling the\ntime difference between any two marked points to be calculated.\n\n.. note:: This class is initialized automatically by the system so there\n\tis no need to do it manually.\n\nIn addition, the benchmark is always started the moment the framework is\ninvoked, and ended by the output class right before sending the final\nview to the browser, enabling a very accurate timing of the entire\nsystem execution to be shown.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n*************************\nUsing the Benchmark Class\n*************************\n\nThe Benchmark class can be used within your\n:doc:`controllers </general/controllers>`,\n:doc:`views </general/views>`, or your :doc:`models </general/models>`.\nThe process for usage is this:\n\n#. Mark a start point\n#. Mark an end point\n#. Run the \"elapsed time\" function to view the results\n\nHere's an example using real code::\n\n\t$this->benchmark->mark('code_start');\n\n\t// Some code happens here\n\n\t$this->benchmark->mark('code_end');\n\n\techo $this->benchmark->elapsed_time('code_start', 'code_end');\n\n.. note:: The words \"code_start\" and \"code_end\" are arbitrary. They\n\tare simply words used to set two markers. You can use any words you\n\twant, and you can set multiple sets of markers. Consider this example::\n\n\t\t$this->benchmark->mark('dog');\n\n\t\t// Some code happens here\n\n\t\t$this->benchmark->mark('cat');\n\n\t\t// More code happens here\n\n\t\t$this->benchmark->mark('bird');\n\n\t\techo $this->benchmark->elapsed_time('dog', 'cat');\n\t\techo $this->benchmark->elapsed_time('cat', 'bird');\n\t\techo $this->benchmark->elapsed_time('dog', 'bird');\n\n\nProfiling Your Benchmark Points\n===============================\n\nIf you want your benchmark data to be available to the\n:doc:`Profiler </general/profiling>` all of your marked points must\nbe set up in pairs, and each mark point name must end with _start and\n_end. Each pair of points must otherwise be named identically. Example::\n\n\t$this->benchmark->mark('my_mark_start');\n\n\t// Some code happens here...\n\n\t$this->benchmark->mark('my_mark_end');\n\n\t$this->benchmark->mark('another_mark_start');\n\n\t// Some more code happens here...\n\n\t$this->benchmark->mark('another_mark_end');\n\nPlease read the :doc:`Profiler page </general/profiling>` for more\ninformation.\n\nDisplaying Total Execution Time\n===============================\n\nIf you would like to display the total elapsed time from the moment\nCodeIgniter starts to the moment the final output is sent to the\nbrowser, simply place this in one of your view templates::\n\n\t<?php echo $this->benchmark->elapsed_time();?>\n\nYou'll notice that it's the same function used in the examples above to\ncalculate the time between two point, except you are **not** using any\nparameters. When the parameters are absent, CodeIgniter does not stop\nthe benchmark until right before the final output is sent to the\nbrowser. It doesn't matter where you use the function call, the timer\nwill continue to run until the very end.\n\nAn alternate way to show your elapsed time in your view files is to use\nthis pseudo-variable, if you prefer not to use the pure PHP::\n\n\t{elapsed_time}\n\n.. note:: If you want to benchmark anything within your controller\n\tfunctions you must set your own start/end points.\n\nDisplaying Memory Consumption\n=============================\n\nIf your PHP installation is configured with --enable-memory-limit, you\ncan display the amount of memory consumed by the entire system using the\nfollowing code in one of your view file::\n\n\t<?php echo $this->benchmark->memory_usage();?>\n\n.. note:: This function can only be used in your view files. The consumption\n\twill reflect the total memory used by the entire app.\n\nAn alternate way to show your memory usage in your view files is to use\nthis pseudo-variable, if you prefer not to use the pure PHP::\n\n\t{memory_usage}\n\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Benchmark\n\n\t.. php:method:: mark($name)\n\n\t\t:param\tstring\t$name: the name you wish to assign to your marker\n\t\t:rtype:\tvoid\n\n\t\tSets a benchmark marker.\n\n\t.. php:method:: elapsed_time([$point1 = ''[, $point2 = ''[, $decimals = 4]]])\n\n\t\t:param\tstring\t$point1: a particular marked point\n\t\t:param\tstring\t$point2: a particular marked point\n\t\t:param\tint\t$decimals: number of decimal places for precision\n\t\t:returns:\tElapsed time\n\t\t:rtype:\tstring\n\n\t\tCalculates and returns the time difference between two marked points.\n\n\t\tIf the first parameter is empty this function instead returns the\n\t\t``{elapsed_time}`` pseudo-variable. This permits the full system\n\t\texecution time to be shown in a template. The output class will\n\t\tswap the real value for this variable.\n\n\n\t.. php:method:: memory_usage()\n\n\t\t:returns:\tMemory usage info\n\t\t:rtype:\tstring\n\n\t\tSimply returns the ``{memory_usage}`` marker.\n\n\t\tThis permits it to be put it anywhere in a template without the memory\n\t\tbeing calculated until the end. The :doc:`Output Class <output>` will\n\t\tswap the real value for this variable."
  },
  {
    "path": "user_guide_src/source/libraries/caching.rst",
    "content": "##############\nCaching Driver\n##############\n\nCodeIgniter features wrappers around some of the most popular forms of\nfast and dynamic caching. All but file-based caching require specific\nserver requirements, and a Fatal Exception will be thrown if server\nrequirements are not met.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n*************\nExample Usage\n*************\n\nThe following example will load the cache driver, specify `APC <#alternative-php-cache-apc-caching>`_\nas the driver to use, and fall back to file-based caching if APC is not\navailable in the hosting environment.\n\n::\n\n\t$this->load->driver('cache', array('adapter' => 'apc', 'backup' => 'file'));\n\n\tif ( ! $foo = $this->cache->get('foo'))\n\t{\n\t\techo 'Saving to the cache!<br />';\n\t\t$foo = 'foobarbaz!';\n\n\t\t// Save into the cache for 5 minutes\n\t\t$this->cache->save('foo', $foo, 300);\n\t}\n\n\techo $foo;\n\nYou can also prefix cache item names via the **key_prefix** setting, which is useful\nto avoid collisions when you're running multiple applications on the same environment.\n\n::\n\n\t$this->load->driver(\n\t\t'cache',\n\t\tarray('adapter' => 'apc', 'backup' => 'file', 'key_prefix' => 'my_')\n\t);\n\n\t$this->cache->get('foo'); // Will get the cache entry named 'my_foo'\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Cache\n\n\t.. php:method:: is_supported($driver)\n\n\t\t:param\tstring\t$driver: the name of the caching driver\n\t\t:returns:\tTRUE if supported, FALSE if not\n\t\t:rtype:\tbool\n\n\t\tThis method is automatically called when accessing drivers via\n\t\t``$this->cache->get()``. However, if the individual drivers are used,\n\t\tmake sure to call this method to ensure the driver is supported in the\n\t\thosting environment.\n\t\t::\n\n\t\t\tif ($this->cache->apc->is_supported())\n\t\t\t{\n\t\t\t\tif ($data = $this->cache->apc->get('my_cache'))\n\t\t\t\t{\n\t\t\t\t\t// do things.\n\t\t\t\t}\n\t\t\t}\n\n\t.. php:method:: get($id)\n\n\t\t:param\tstring\t$id: Cache item name\n\t\t:returns:\tItem value or FALSE if not found\n\t\t:rtype:\tmixed\n\n\t\tThis method will attempt to fetch an item from the cache store. If the\n\t\titem does not exist, the method will return FALSE.\n\t\t::\n\n\t\t\t$foo = $this->cache->get('my_cached_item');\n\n\t.. php:method:: save($id, $data[, $ttl = 60[, $raw = FALSE]])\n\n\t\t:param\tstring\t$id: Cache item name\n\t\t:param\tmixed\t$data: the data to save\n\t\t:param\tint\t$ttl: Time To Live, in seconds (default 60)\n\t\t:param\tbool\t$raw: Whether to store the raw value\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tstring\n\n\t\tThis method will save an item to the cache store. If saving fails, the\n\t\tmethod will return FALSE.\n\t\t::\n\n\t\t\t$this->cache->save('cache_item_id', 'data_to_cache');\n\n\t\t.. note:: The ``$raw`` parameter is only utilized by APC, APCu and Memcache,\n\t\t\tin order to allow usage of ``increment()`` and ``decrement()``.\n\n\t.. php:method:: delete($id)\n\n\t\t:param\tstring\t$id: name of cached item\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tThis method will delete a specific item from the cache store. If item\n\t\tdeletion fails, the method will return FALSE.\n\t\t::\n\n\t\t\t$this->cache->delete('cache_item_id');\n\n\t.. php:method:: increment($id[, $offset = 1])\n\n\t\t:param\tstring\t$id: Cache ID\n\t\t:param\tint\t$offset: Step/value to add\n\t\t:returns:\tNew value on success, FALSE on failure\n\t\t:rtype:\tmixed\n\n\t\tPerforms atomic incrementation of a raw stored value.\n\t\t::\n\n\t\t\t// 'iterator' has a value of 2\n\n\t\t\t$this->cache->increment('iterator'); // 'iterator' is now 3\n\n\t\t\t$this->cache->increment('iterator', 3); // 'iterator' is now 6\n\n\t.. php:method:: decrement($id[, $offset = 1])\n\n\t\t:param\tstring\t$id: Cache ID\n\t\t:param\tint\t$offset: Step/value to reduce by\n\t\t:returns:\tNew value on success, FALSE on failure\n\t\t:rtype:\tmixed\n\n\t\tPerforms atomic decrementation of a raw stored value.\n\t\t::\n\n\t\t\t// 'iterator' has a value of 6\n\n\t\t\t$this->cache->decrement('iterator'); // 'iterator' is now 5\n\n\t\t\t$this->cache->decrement('iterator', 2); // 'iterator' is now 3\n\n\t.. php:method:: clean()\n\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tThis method will 'clean' the entire cache. If the deletion of the\n\t\tcache files fails, the method will return FALSE.\n\t\t::\n\n\t\t\t$this->cache->clean();\n\n\t.. php:method:: cache_info()\n\n\t\t:returns:\tInformation on the entire cache database\n\t\t:rtype:\tmixed\n\n\t\tThis method will return information on the entire cache.\n\t\t::\n\n\t\t\tvar_dump($this->cache->cache_info());\n\n\t\t.. note:: The information returned and the structure of the data is dependent\n\t\t\ton which adapter is being used.\n\n\t.. php:method:: get_metadata($id)\n\n\t\t:param\tstring\t$id: Cache item name\n\t\t:returns:\tMetadata for the cached item\n\t\t:rtype:\tmixed\n\n\t\tThis method will return detailed information on a specific item in the\n\t\tcache.\n\t\t::\n\n\t\t\tvar_dump($this->cache->get_metadata('my_cached_item'));\n\n\t\t.. note:: The information returned and the structure of the data is dependent\n\t\t\ton which adapter is being used.\n\n\t.. php:method:: get_loaded_driver()\n\n\t\t:returns:\tLoaded driver name after initialization ('apc', 'apcu', 'dummy', 'file', 'memcached', 'redis' or 'wincache')\n\t\t:rtype:\tstring\n\n\t\tThis method will return the caching driver currently used after initialization.\n\t\t::\n\n\t\t\techo $this->cache->get_loaded_driver(); // Will return something like \"file\"\n\n*******\nDrivers\n*******\n\nAlternative PHP Cache (APC) Caching\n===================================\n\nAll of the methods listed above can be accessed without passing a\nspecific adapter to the driver loader as follows::\n\n\t$this->load->driver('cache');\n\t$this->cache->apc->save('foo', 'bar', 10);\n\nFor more information on APC, please see\n`https://php.net/apc <https://php.net/apc>`_.\n\nAPC User Cache (APCu) Caching\n=============================\n\nAll of the methods listed above can be accessed without passing a\nspecific adapter to the driver loader as follows::\n\n\t$this->load->driver('cache');\n\t$this->cache->apcu->save('foo', 'bar', 10);\n\nFor more information on APCu, please see\n`https://php.net/apcu <https://php.net/apcu>`_.\n\nFile-based Caching\n==================\n\nUnlike caching from the Output Class, the driver file-based caching\nallows for pieces of view files to be cached. Use this with care, and\nmake sure to benchmark your application, as a point can come where disk\nI/O will negate positive gains by caching.\n\nAll of the methods listed above can be accessed without passing a\nspecific adapter to the driver loader as follows::\n\n\t$this->load->driver('cache');\n\t$this->cache->file->save('foo', 'bar', 10);\n\nMemcached Caching\n=================\n\nMultiple Memcached servers can be specified in the memcached.php\nconfiguration file, located in the _application/config/* directory.\n\nAll of the methods listed above can be accessed without passing a\nspecific adapter to the driver loader as follows::\n\n\t$this->load->driver('cache');\n\t$this->cache->memcached->save('foo', 'bar', 10);\n\nFor more information on Memcached, please see\n`https://php.net/memcached <https://php.net/memcached>`_.\n\nWinCache Caching\n================\n\nUnder Windows, you can also utilize the WinCache driver.\n\nAll of the methods listed above can be accessed without passing a\nspecific adapter to the driver loader as follows::\n\n\t$this->load->driver('cache');\n\t$this->cache->wincache->save('foo', 'bar', 10);\n\nFor more information on WinCache, please see\n`https://php.net/wincache <https://php.net/wincache>`_.\n\nRedis Caching\n=============\n\nRedis is an in-memory key-value store which can operate in LRU cache mode. \nTo use it, you need `Redis server and phpredis PHP extension <https://github.com/phpredis/phpredis>`_.\n\nConfig options to connect to redis server must be stored in the application/config/redis.php file.\nAvailable options are::\n\t\n\t$config['host'] = '127.0.0.1';\n\t$config['password'] = NULL;\n\t$config['port'] = 6379;\n\t$config['timeout'] = 0;\n\nAll of the methods listed above can be accessed without passing a\nspecific adapter to the driver loader as follows::\n\n\t$this->load->driver('cache');\n\t$this->cache->redis->save('foo', 'bar', 10);\n\nFor more information on Redis, please see\n`https://redis.io <https://redis.io>`_.\n\nDummy Cache\n===========\n\nThis is a caching backend that will always 'miss.' It stores no data,\nbut lets you keep your caching code in place in environments that don't\nsupport your chosen cache.\n"
  },
  {
    "path": "user_guide_src/source/libraries/calendar.rst",
    "content": "#################\nCalendaring Class\n#################\n\nThe Calendar class enables you to dynamically create calendars. Your\ncalendars can be formatted through the use of a calendar template,\nallowing 100% control over every aspect of its design. In addition, you\ncan pass data to your calendar cells.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n***************************\nUsing the Calendaring Class\n***************************\n\nInitializing the Class\n======================\n\nLike most other classes in CodeIgniter, the Calendar class is\ninitialized in your controller using the $this->load->library function::\n\n\t$this->load->library('calendar');\n\nOnce loaded, the Calendar object will be available using::\n\n\t$this->calendar\n\nDisplaying a Calendar\n=====================\n\nHere is a very simple example showing how you can display a calendar::\n\n\t$this->load->library('calendar');\n\techo $this->calendar->generate();\n\nThe above code will generate a calendar for the current month/year based\non your server time. To show a calendar for a specific month and year\nyou will pass this information to the calendar generating function::\n\n\t$this->load->library('calendar');\n\techo $this->calendar->generate(2006, 6);\n\nThe above code will generate a calendar showing the month of June in\n2006. The first parameter specifies the year, the second parameter\nspecifies the month.\n\nPassing Data to your Calendar Cells\n===================================\n\nTo add data to your calendar cells involves creating an associative\narray in which the keys correspond to the days you wish to populate and\nthe array value contains the data. The array is passed to the third\nparameter of the calendar generating function. Consider this example::\n\n\t$this->load->library('calendar');\n\n\t$data = array(\n\t\t3  => 'http://example.com/news/article/2006/06/03/',\n\t\t7  => 'http://example.com/news/article/2006/06/07/',\n\t\t13 => 'http://example.com/news/article/2006/06/13/',\n\t\t26 => 'http://example.com/news/article/2006/06/26/'\n\t);\n\n\techo $this->calendar->generate(2006, 6, $data);\n\nUsing the above example, day numbers 3, 7, 13, and 26 will become links\npointing to the URLs you've provided.\n\n.. note:: By default it is assumed that your array will contain links.\n\tIn the section that explains the calendar template below you'll see how\n\tyou can customize how data passed to your cells is handled so you can\n\tpass different types of information.\n\nSetting Display Preferences\n===========================\n\nThere are seven preferences you can set to control various aspects of\nthe calendar. Preferences are set by passing an array of preferences in\nthe second parameter of the loading function. Here is an example::\n\n\t$prefs = array(\n\t\t'start_day'    => 'saturday',\n\t\t'month_type'   => 'long',\n\t\t'day_type'     => 'short'\n\t);\n\n\t$this->load->library('calendar', $prefs);\n\n\techo $this->calendar->generate();\n\nThe above code would start the calendar on saturday, use the \"long\"\nmonth heading, and the \"short\" day names. More information regarding\npreferences below.\n\n======================  =================  ============================================  ===================================================================\nPreference              Default            Options                                       Description\n======================  =================  ============================================  ===================================================================\n**template**           \tNone               None                                          A string or array containing your calendar template.\n\t\t\t\t\t\t\t\t\t\t\t   See the template section below.\n**local_time**        \ttime()             None                                          A Unix timestamp corresponding to the current time.\n**start_day**           sunday             Any week day (sunday, monday, tuesday, etc.)  Sets the day of the week the calendar should start on.\n**month_type**          long               long, short                                   Determines what version of the month name to use in the header.\n\t\t\t\t\t\t\t\t\t\t\t   long = January, short = Jan.\n**day_type**            abr                long, short, abr                              Determines what version of the weekday names to use in\n\t\t\t\t\t\t\t\t\t\t\t   the column headers. long = Sunday, short = Sun, abr = Su.\n**show_next_prev**      FALSE              TRUE/FALSE (boolean)                          Determines whether to display links allowing you to toggle\n\t\t\t\t\t\t\t\t\t\t\t   to next/previous months. See information on this feature below.\n**next_prev_url**       controller/method  A URL                                         Sets the basepath used in the next/previous calendar links.\n**show_other_days**     FALSE              TRUE/FALSE (boolean)                          Determines whether to display days of other months that share the\n\t\t\t\t\t\t\t\t\t\t\t   first or last week of the calendar month.\n======================  =================  ============================================  ===================================================================\n\n\nShowing Next/Previous Month Links\n=================================\n\nTo allow your calendar to dynamically increment/decrement via the\nnext/previous links requires that you set up your calendar code similar\nto this example::\n\n\t$prefs = array(\n\t\t'show_next_prev'  => TRUE,\n\t\t'next_prev_url'   => 'http://example.com/index.php/calendar/show/'\n\t);\n\n\t$this->load->library('calendar', $prefs);\n\n\techo $this->calendar->generate($this->uri->segment(3), $this->uri->segment(4));\n\nYou'll notice a few things about the above example:\n\n-  You must set the \"show_next_prev\" to TRUE.\n-  You must supply the URL to the controller containing your calendar in\n   the \"next_prev_url\" preference. If you don't, it will be set to the current\n   *controller/method*.\n-  You must supply the \"year\" and \"month\" to the calendar generating\n   function via the URI segments where they appear (Note: The calendar\n   class automatically adds the year/month to the base URL you\n   provide.).\n\nCreating a Calendar Template\n============================\n\nBy creating a calendar template you have 100% control over the design of\nyour calendar. Using the string method, each component of your calendar\nwill be placed within a pair of pseudo-variables as shown here::\n\n\t$prefs['template'] = '\n\n\t\t{table_open}<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">{/table_open}\n\n\t\t{heading_row_start}<tr>{/heading_row_start}\n\n\t\t{heading_previous_cell}<th><a href=\"{previous_url}\">&lt;&lt;</a></th>{/heading_previous_cell}\n\t\t{heading_title_cell}<th colspan=\"{colspan}\">{heading}</th>{/heading_title_cell}\n\t\t{heading_next_cell}<th><a href=\"{next_url}\">&gt;&gt;</a></th>{/heading_next_cell}\n\n\t\t{heading_row_end}</tr>{/heading_row_end}\n\n\t\t{week_row_start}<tr>{/week_row_start}\n\t\t{week_day_cell}<td>{week_day}</td>{/week_day_cell}\n\t\t{week_row_end}</tr>{/week_row_end}\n\n\t\t{cal_row_start}<tr>{/cal_row_start}\n\t\t{cal_cell_start}<td>{/cal_cell_start}\n\t\t{cal_cell_start_today}<td>{/cal_cell_start_today}\n\t\t{cal_cell_start_other}<td class=\"other-month\">{/cal_cell_start_other}\n\n\t\t{cal_cell_content}<a href=\"{content}\">{day}</a>{/cal_cell_content}\n\t\t{cal_cell_content_today}<div class=\"highlight\"><a href=\"{content}\">{day}</a></div>{/cal_cell_content_today}\n\n\t\t{cal_cell_no_content}{day}{/cal_cell_no_content}\n\t\t{cal_cell_no_content_today}<div class=\"highlight\">{day}</div>{/cal_cell_no_content_today}\n\n\t\t{cal_cell_blank}&nbsp;{/cal_cell_blank}\n\n\t\t{cal_cell_other}{day}{/cal_cel_other}\n\n\t\t{cal_cell_end}</td>{/cal_cell_end}\n\t\t{cal_cell_end_today}</td>{/cal_cell_end_today}\n\t\t{cal_cell_end_other}</td>{/cal_cell_end_other}\n\t\t{cal_row_end}</tr>{/cal_row_end}\n\n\t\t{table_close}</table>{/table_close}\n\t';\n\n\t$this->load->library('calendar', $prefs);\n\n\techo $this->calendar->generate();\n\nUsing the array method, you will pass `key => value` pairs. You can pass as\nmany or as few values as you'd like. Omitted keys will use the default values\ninherited in the calendar class.\n\nExample::\n\n\t$prefs['template'] = array(\n\t\t'table_open'           => '<table class=\"calendar\">',\n\t\t'cal_cell_start'       => '<td class=\"day\">',\n\t\t'cal_cell_start_today' => '<td class=\"today\">'\n\t);\n    \n\t$this->load->library('calendar', $prefs);\n    \n\techo $this->calendar->generate();\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Calendar\n\n\t.. php:method:: initialize([$config = array()])\n\n\t\t:param\tarray\t$config: Configuration parameters\n\t\t:returns:\tCI_Calendar instance (method chaining)\n\t\t:rtype:\tCI_Calendar\n\n\t\tInitializes the Calendaring preferences. Accepts an associative array as input, containing display preferences.\n\n\t.. php:method:: generate([$year = ''[, $month = ''[, $data = array()]]])\n\n\t\t:param\tint\t$year: Year\n\t\t:param\tint\t$month: Month\n\t\t:param\tarray\t$data: Data to be shown in the calendar cells\n\t\t:returns:\tHTML-formatted calendar\n\t\t:rtype:\tstring\n\n\t\tGenerate the calendar.\n\n\n\t.. php:method:: get_month_name($month)\n\n\t\t:param\tint\t$month: Month\n\t\t:returns:\tMonth name\n\t\t:rtype:\tstring\n\n\t\tGenerates a textual month name based on the numeric month provided.\n\n\t.. php:method:: get_day_names($day_type = '')\n\n\t\t:param\tstring\t$day_type: 'long', 'short', or 'abr'\n\t\t:returns:\tArray of day names\n\t\t:rtype:\tarray\n\n\t\tReturns an array of day names (Sunday, Monday, etc.) based on the type\n\t\tprovided. Options: long, short, abr. If no ``$day_type`` is provided (or\n\t\tif an invalid type is provided) this method will return the \"abbreviated\"\n\t\tstyle.\n\n\t.. php:method:: adjust_date($month, $year)\n\n\t\t:param\tint\t$month: Month\n\t\t:param\tint\t$year: Year\n\t\t:returns:\tAn associative array containing month and year\n\t\t:rtype:\tarray\n\n\t\tThis method makes sure that you have a valid month/year. For example, if\n\t\tyou submit 13 as the month, the year will increment and the month will\n\t\tbecome January::\n\n\t\t\tprint_r($this->calendar->adjust_date(13, 2014));\n\n\t\toutputs::\n\n\t\t\tArray\n\t\t\t(    \n\t\t\t\t[month] => '01'\n\t\t\t\t[year] => '2015'\n\t\t\t)\n\n\t.. php:method:: get_total_days($month, $year)\n\n\t\t:param\tint\t$month: Month\n\t\t:param\tint\t$year: Year\n\t\t:returns:\tCount of days in the specified month\n\t\t:rtype:\tint\n\n\t\tTotal days in a given month::\n\n\t\t\techo $this->calendar->get_total_days(2, 2012);\n\t\t\t// 29\n\n\t\t.. note:: This method is an alias for :doc:`Date Helper\n\t\t\t<../helpers/date_helper>` function :php:func:`days_in_month()`.\n\n\t.. php:method:: default_template()\n\n\t\t:returns:\tAn array of template values\n\t\t:rtype:\tarray\n\n\t\tSets the default template. This method is used when you have not created\n\t\tyour own template.\n\n\n\t.. php:method:: parse_template()\n\n\t\t:returns:\tCI_Calendar instance (method chaining)\n\t\t:rtype:\tCI_Calendar\n\n\t\tHarvests the data within the template ``{pseudo-variables}`` used to\n\t\tdisplay the calendar.\n"
  },
  {
    "path": "user_guide_src/source/libraries/config.rst",
    "content": "############\nConfig Class\n############\n\nThe Config class provides a means to retrieve configuration preferences.\nThese preferences can come from the default config file\n(application/config/config.php) or from your own custom config files.\n\n.. note:: This class is initialized automatically by the system so there\n\tis no need to do it manually.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n*****************************\nWorking with the Config Class\n*****************************\n\nAnatomy of a Config File\n========================\n\nBy default, CodeIgniter has one primary config file, located at\napplication/config/config.php. If you open the file using your text\neditor you'll see that config items are stored in an array called\n$config.\n\nYou can add your own config items to this file, or if you prefer to keep\nyour configuration items separate (assuming you even need config items),\nsimply create your own file and save it in config folder.\n\n.. note:: If you do create your own config files use the same format as\n\tthe primary one, storing your items in an array called $config.\n\tCodeIgniter will intelligently manage these files so there will be no\n\tconflict even though the array has the same name (assuming an array\n\tindex is not named the same as another).\n\nLoading a Config File\n=====================\n\n.. note::\n\tCodeIgniter automatically loads the primary config file\n\t(application/config/config.php), so you will only need to load a config\n\tfile if you have created your own.\n\nThere are two ways to load a config file:\n\nManual Loading\n**************\n\nTo load one of your custom config files you will use the following\nfunction within the :doc:`controller </general/controllers>` that\nneeds it::\n\n\t$this->config->load('filename');\n\nWhere filename is the name of your config file, without the .php file\nextension.\n\nIf you need to load multiple config files normally they will be\nmerged into one master config array. Name collisions can occur,\nhowever, if you have identically named array indexes in different\nconfig files. To avoid collisions you can set the second parameter to\nTRUE and each config file will be stored in an array index\ncorresponding to the name of the config file. Example::\n\n\t// Stored in an array with this prototype: $this->config['blog_settings'] = $config\n\t$this->config->load('blog_settings', TRUE);\n\nPlease see the section entitled Fetching Config Items below to learn\nhow to retrieve config items set this way.\n\nThe third parameter allows you to suppress errors in the event that a\nconfig file does not exist::\n\n\t$this->config->load('blog_settings', FALSE, TRUE);\n\nAuto-loading\n************\n\nIf you find that you need a particular config file globally, you can\nhave it loaded automatically by the system. To do this, open the\n**autoload.php** file, located at application/config/autoload.php,\nand add your config file as indicated in the file.\n\n\nFetching Config Items\n=====================\n\nTo retrieve an item from your config file, use the following function::\n\n\t$this->config->item('item_name');\n\nWhere item_name is the $config array index you want to retrieve. For\nexample, to fetch your language choice you'll do this::\n\n\t$lang = $this->config->item('language');\n\nThe function returns NULL if the item you are trying to fetch\ndoes not exist.\n\nIf you are using the second parameter of the $this->config->load\nfunction in order to assign your config items to a specific index you\ncan retrieve it by specifying the index name in the second parameter of\nthe $this->config->item() function. Example::\n\n\t// Loads a config file named blog_settings.php and assigns it to an index named \"blog_settings\"\n\t$this->config->load('blog_settings', TRUE);\n\n\t// Retrieve a config item named site_name contained within the blog_settings array\n\t$site_name = $this->config->item('site_name', 'blog_settings');\n\n\t// An alternate way to specify the same item:\n\t$blog_config = $this->config->item('blog_settings');\n\t$site_name = $blog_config['site_name'];\n\nSetting a Config Item\n=====================\n\nIf you would like to dynamically set a config item or change an existing\none, you can do so using::\n\n\t$this->config->set_item('item_name', 'item_value');\n\nWhere item_name is the $config array index you want to change, and\nitem_value is its value.\n\n.. _config-environments:\n\nEnvironments\n============\n\nYou may load different configuration files depending on the current\nenvironment. The ENVIRONMENT constant is defined in index.php, and is\ndescribed in detail in the :doc:`Handling\nEnvironments </general/environments>` section.\n\nTo create an environment-specific configuration file, create or copy a\nconfiguration file in application/config/{ENVIRONMENT}/{FILENAME}.php\n\nFor example, to create a production-only config.php, you would:\n\n#. Create the directory application/config/production/\n#. Copy your existing config.php into the above directory\n#. Edit application/config/production/config.php so it contains your\n   production settings\n\nWhen you set the ENVIRONMENT constant to 'production', the settings for\nyour new production-only config.php will be loaded.\n\nYou can place the following configuration files in environment-specific\nfolders:\n\n-  Default CodeIgniter configuration files\n-  Your own custom configuration files\n\n.. note::\n\tCodeIgniter always loads the global config file first (i.e., the one in application/config/),\n\tthen tries to load the configuration files for the current environment.\n\tThis means you are not obligated to place **all** of your configuration files in an\n\tenvironment folder. Only the files that change per environment. Additionally you don't\n\thave to copy **all** the config items in the environment config file. Only the config items\n\tthat you wish to change for your environment. The config items declared in your environment\n\tfolders always overwrite those in your global config files.\n\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Config\n\n\t.. attribute:: $config\n\n\t\tArray of all loaded config values\n\n\t.. attribute:: $is_loaded\n\n\t\tArray of all loaded config files\n\n\n\t.. php:method:: item($item[, $index=''])\n\n\t\t:param\tstring\t$item: Config item name\n\t\t:param\tstring\t$index: Index name\n\t\t:returns:\tConfig item value or NULL if not found\n\t\t:rtype:\tmixed\n\n\t\tFetch a config file item.\n\n\t.. php:method:: set_item($item, $value)\n\n\t\t:param\tstring\t$item: Config item name\n\t\t:param\tstring\t$value: Config item value\n\t\t:rtype:\tvoid\n\n\t\tSets a config file item to the specified value.\n\n\t.. php:method:: slash_item($item)\n\n\t\t:param\tstring\t$item: config item name\n\t\t:returns:\tConfig item value with a trailing forward slash or NULL if not found\n\t\t:rtype:\tmixed\n\n\t\tThis method is identical to ``item()``, except it appends a forward\n\t\tslash to the end of the item, if it exists.\n\n\t.. php:method:: load([$file = ''[, $use_sections = FALSE[, $fail_gracefully = FALSE]]])\n\n\t\t:param\tstring\t$file: Configuration file name\n\t\t:param\tbool\t$use_sections: Whether config values should be loaded into their own section (index of the main config array)\n\t\t:param\tbool\t$fail_gracefully: Whether to return FALSE or to display an error message\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tLoads a configuration file.\n\n\t.. php:method:: site_url()\n\n\t\t:returns:\tSite URL\n\t\t:rtype:\tstring\n\n\t\tThis method retrieves the URL to your site, along with the \"index\" value\n\t\tyou've specified in the config file.\n\n\t\tThis method is normally accessed via the corresponding functions in the\n\t\t:doc:`URL Helper </helpers/url_helper>`.\n\n\t.. php:method:: base_url()\n\n\t\t:returns:\tBase URL\n\t\t:rtype:\tstring\n\n\t\tThis method retrieves the URL to your site, plus an optional path such\n\t\tas to a stylesheet or image.\n\n\t\tThis method is normally accessed via the corresponding functions in the\n\t\t:doc:`URL Helper </helpers/url_helper>`.\n"
  },
  {
    "path": "user_guide_src/source/libraries/email.rst",
    "content": "###########\nEmail Class\n###########\n\nCodeIgniter's robust Email Class supports the following features:\n\n-  Multiple Protocols: Mail, Sendmail, and SMTP\n-  TLS and SSL Encryption for SMTP\n-  Multiple recipients\n-  CC and BCCs\n-  HTML or Plaintext email\n-  Attachments\n-  Word wrapping\n-  Priorities\n-  BCC Batch Mode, enabling large email lists to be broken into small\n   BCC batches.\n-  Email Debugging tools\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n***********************\nUsing the Email Library\n***********************\n\nSending Email\n=============\n\nSending email is not only simple, but you can configure it on the fly or\nset your preferences in a config file.\n\nHere is a basic example demonstrating how you might send email. Note:\nThis example assumes you are sending the email from one of your\n:doc:`controllers <../general/controllers>`.\n\n::\n\n\t$this->load->library('email');\n\n\t$this->email->from('your@example.com', 'Your Name');\n\t$this->email->to('someone@example.com');\n\t$this->email->cc('another@another-example.com');\n\t$this->email->bcc('them@their-example.com');\n\n\t$this->email->subject('Email Test');\n\t$this->email->message('Testing the email class.');\n\n\t$this->email->send();\n\nSetting Email Preferences\n=========================\n\nThere are 21 different preferences available to tailor how your email\nmessages are sent. You can either set them manually as described here,\nor automatically via preferences stored in your config file, described\nbelow:\n\nPreferences are set by passing an array of preference values to the\nemail initialize method. Here is an example of how you might set some\npreferences::\n\n\t$config['protocol'] = 'sendmail';\n\t$config['mailpath'] = '/usr/sbin/sendmail';\n\t$config['charset'] = 'iso-8859-1';\n\t$config['wordwrap'] = TRUE;\n\n\t$this->email->initialize($config);\n\n.. note:: Most of the preferences have default values that will be used\n\tif you do not set them.\n\nSetting Email Preferences in a Config File\n------------------------------------------\n\nIf you prefer not to set preferences using the above method, you can\ninstead put them into a config file. Simply create a new file called the\nemail.php, add the $config array in that file. Then save the file at\nconfig/email.php and it will be used automatically. You will NOT need to\nuse the ``$this->email->initialize()`` method if you save your\npreferences in a config file.\n\nEmail Preferences\n=================\n\nThe following is a list of all the preferences that can be set when\nsending email.\n\n=================== ====================== ============================ =======================================================================\nPreference          Default Value          Options                      Description\n=================== ====================== ============================ =======================================================================\n**useragent**       CodeIgniter            None                         The \"user agent\".\n**protocol**        mail                   mail, sendmail, or smtp      The mail sending protocol.\n**mailpath**        /usr/sbin/sendmail     None                         The server path to Sendmail.\n**smtp_host**       No Default             None                         SMTP Server Address.\n**smtp_user**       No Default             None                         SMTP Username.\n**smtp_pass**       No Default             None                         SMTP Password.\n**smtp_port**       25                     None                         SMTP Port.\n**smtp_timeout**    5                      None                         SMTP Timeout (in seconds).\n**smtp_keepalive**  FALSE                  TRUE or FALSE (boolean)      Enable persistent SMTP connections.\n**smtp_crypto**     No Default             tls or ssl                   SMTP Encryption\n**wordwrap**        TRUE                   TRUE or FALSE (boolean)      Enable word-wrap.\n**wrapchars**       76                                                  Character count to wrap at.\n**mailtype**        text                   text or html                 Type of mail. If you send HTML email you must send it as a complete web\n                                                                        page. Make sure you don't have any relative links or relative image\n                                                                        paths otherwise they will not work.\n**charset**         ``$config['charset']``                              Character set (utf-8, iso-8859-1, etc.).\n**validate**        TRUE                   TRUE or FALSE (boolean)      Whether to validate the email address.\n**priority**        3                      1, 2, 3, 4, 5                Email Priority. 1 = highest. 5 = lowest. 3 = normal.\n**crlf**            \\\\n                    \"\\\\r\\\\n\" or \"\\\\n\" or \"\\\\r\"   Newline character. (Use \"\\\\r\\\\n\" to comply with RFC 822).\n**newline**         \\\\n                    \"\\\\r\\\\n\" or \"\\\\n\" or \"\\\\r\"   Newline character. (Use \"\\\\r\\\\n\" to comply with RFC 822).\n**bcc_batch_mode**  FALSE                  TRUE or FALSE (boolean)      Enable BCC Batch Mode.\n**bcc_batch_size**  200                    None                         Number of emails in each BCC batch.\n**dsn**             FALSE                  TRUE or FALSE (boolean)      Enable notify message from server\n=================== ====================== ============================ =======================================================================\n\nOverriding Word Wrapping\n========================\n\nIf you have word wrapping enabled (recommended to comply with RFC 822)\nand you have a very long link in your email it can get wrapped too,\ncausing it to become un-clickable by the person receiving it.\nCodeIgniter lets you manually override word wrapping within part of your\nmessage like this::\n\n\tThe text of your email that\n\tgets wrapped normally.\n\n\t{unwrap}http://example.com/a_long_link_that_should_not_be_wrapped.html{/unwrap}\n\n\tMore text that will be\n\twrapped normally.\n\n\nPlace the item you do not want word-wrapped between: {unwrap} {/unwrap}\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Email\n\n\t.. php:method:: from($from[, $name = ''[, $return_path = NULL]])\n\n\t\t:param\tstring\t$from: \"From\" e-mail address\n\t\t:param\tstring\t$name: \"From\" display name\n\t\t:param\tstring\t$return_path: Optional email address to redirect undelivered e-mail to\n\t\t:returns:\tCI_Email instance (method chaining)\n\t\t:rtype:\tCI_Email\n\n\t\tSets the email address and name of the person sending the email::\n\n\t\t\t$this->email->from('you@example.com', 'Your Name');\n\n\t\tYou can also set a Return-Path, to help redirect undelivered mail::\n\n\t\t\t$this->email->from('you@example.com', 'Your Name', 'returned_emails@example.com');\n\n\t\t.. note:: Return-Path can't be used if you've configured 'smtp' as\n\t\t\tyour protocol.\n\n\t.. php:method:: reply_to($replyto[, $name = ''])\n\n\t\t:param\tstring\t$replyto: E-mail address for replies\n\t\t:param\tstring\t$name: Display name for the reply-to e-mail address\n\t\t:returns:\tCI_Email instance (method chaining)\n\t\t:rtype:\tCI_Email\n\n\t\tSets the reply-to address. If the information is not provided the\n\t\tinformation in the :meth:from method is used. Example::\n\n\t\t\t$this->email->reply_to('you@example.com', 'Your Name');\n\n\t.. php:method:: to($to)\n\n\t\t:param\tmixed\t$to: Comma-delimited string or an array of e-mail addresses\n\t\t:returns:\tCI_Email instance (method chaining)\n\t\t:rtype:\tCI_Email\n\n\t\tSets the email address(s) of the recipient(s). Can be a single e-mail,\n\t\ta comma-delimited list or an array::\n\n\t\t\t$this->email->to('someone@example.com');\n\n\t\t::\n\n\t\t\t$this->email->to('one@example.com, two@example.com, three@example.com');\n\n\t\t::\n\n\t\t\t$this->email->to(\n\t\t\t\tarray('one@example.com', 'two@example.com', 'three@example.com')\n\t\t\t);\n\n\t.. php:method:: cc($cc)\n\n\t\t:param\tmixed\t$cc: Comma-delimited string or an array of e-mail addresses\n\t\t:returns:\tCI_Email instance (method chaining)\n\t\t:rtype:\tCI_Email\n\n\t\tSets the CC email address(s). Just like the \"to\", can be a single e-mail,\n\t\ta comma-delimited list or an array.\n\n\t.. php:method:: bcc($bcc[, $limit = ''])\n\n\t\t:param\tmixed\t$bcc: Comma-delimited string or an array of e-mail addresses\n\t\t:param\tint\t$limit: Maximum number of e-mails to send per batch\n\t\t:returns:\tCI_Email instance (method chaining)\n\t\t:rtype:\tCI_Email\n\n\t\tSets the BCC email address(s). Just like the ``to()`` method, can be a single\n\t\te-mail, a comma-delimited list or an array.\n\n\t\tIf ``$limit`` is set, \"batch mode\" will be enabled, which will send\n\t\tthe emails to batches, with each batch not exceeding the specified\n\t\t``$limit``.\n\n\t.. php:method:: subject($subject)\n\n\t\t:param\tstring\t$subject: E-mail subject line\n\t\t:returns:\tCI_Email instance (method chaining)\n\t\t:rtype:\tCI_Email\n\n\t\tSets the email subject::\n\n\t\t\t$this->email->subject('This is my subject');\n\n\t.. php:method:: message($body)\n\n\t\t:param\tstring\t$body: E-mail message body\n\t\t:returns:\tCI_Email instance (method chaining)\n\t\t:rtype:\tCI_Email\n\n\t\tSets the e-mail message body::\n\n\t\t\t$this->email->message('This is my message');\n\n\t.. php:method:: set_alt_message($str)\n\n\t\t:param\tstring\t$str: Alternative e-mail message body\n\t\t:returns:\tCI_Email instance (method chaining)\n\t\t:rtype:\tCI_Email\n\n\t\tSets the alternative e-mail message body::\n\n\t\t\t$this->email->set_alt_message('This is the alternative message');\n\n\t\tThis is an optional message string which can be used if you send\n\t\tHTML formatted email. It lets you specify an alternative message\n\t\twith no HTML formatting which is added to the header string for\n\t\tpeople who do not accept HTML email. If you do not set your own\n\t\tmessage CodeIgniter will extract the message from your HTML email\n\t\tand strip the tags.\n\n\t.. php:method:: set_header($header, $value)\n\n\t\t:param\tstring\t$header: Header name\n\t\t:param\tstring\t$value: Header value\n\t\t:returns:\tCI_Email instance (method chaining)\n\t\t:rtype: CI_Email\n\n\t\tAppends additional headers to the e-mail::\n\n\t\t\t$this->email->set_header('Header1', 'Value1');\n\t\t\t$this->email->set_header('Header2', 'Value2');\n\n\t.. php:method:: clear([$clear_attachments = FALSE])\n\n\t\t:param\tbool\t$clear_attachments: Whether or not to clear attachments\n\t\t:returns:\tCI_Email instance (method chaining)\n\t\t:rtype: CI_Email\n\n\t\tInitializes all the email variables to an empty state. This method\n\t\tis intended for use if you run the email sending method in a loop,\n\t\tpermitting the data to be reset between cycles.\n\n\t\t::\n\n\t\t\tforeach ($list as $name => $address)\n\t\t\t{\n\t\t\t\t$this->email->clear();\n\n\t\t\t\t$this->email->to($address);\n\t\t\t\t$this->email->from('your@example.com');\n\t\t\t\t$this->email->subject('Here is your info '.$name);\n\t\t\t\t$this->email->message('Hi '.$name.' Here is the info you requested.');\n\t\t\t\t$this->email->send();\n\t\t\t}\n\n\t\tIf you set the parameter to TRUE any attachments will be cleared as\n\t\twell::\n\n\t\t\t$this->email->clear(TRUE);\n\n\t.. php:method:: send([$auto_clear = TRUE])\n\n\t\t:param\tbool\t$auto_clear: Whether to clear message data automatically\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tThe e-mail sending method. Returns boolean TRUE or FALSE based on\n\t\tsuccess or failure, enabling it to be used conditionally::\n\n\t\t\tif ( ! $this->email->send())\n\t\t\t{\n\t\t\t\t// Generate error\n\t\t\t}\n\n\t\tThis method will automatically clear all parameters if the request was\n\t\tsuccessful. To stop this behaviour pass FALSE::\n\n\t\t\tif ($this->email->send(FALSE))\n\t\t\t{\n\t\t\t\t// Parameters won't be cleared\n\t\t\t}\n\n\t\t.. note:: In order to use the ``print_debugger()`` method, you need\n\t\t\tto avoid clearing the email parameters.\n\n\t\t.. note:: If ``batch_bcc_mode`` is enabled, and there are more than\n\t\t\t``batch_bcc_size`` recipients, this method will always return\n\t\t\tboolean ``TRUE``.\n\n\t.. php:method:: attach($filename[, $disposition = ''[, $newname = NULL[, $mime = '']]])\n\n\t\t:param\tstring\t$filename: File name\n\t\t:param\tstring\t$disposition: 'disposition' of the attachment. Most\n\t\t\temail clients make their own decision regardless of the MIME\n\t\t\tspecification used here. https://www.iana.org/assignments/cont-disp/cont-disp.xhtml\n\t\t:param\tstring\t$newname: Custom file name to use in the e-mail\n\t\t:param\tstring\t$mime: MIME type to use (useful for buffered data)\n\t\t:returns:\tCI_Email instance (method chaining)\n\t\t:rtype:\tCI_Email\n\n\t\tEnables you to send an attachment. Put the file path/name in the first\n\t\tparameter. For multiple attachments use the method multiple times.\n\t\tFor example::\n\n\t\t\t$this->email->attach('/path/to/photo1.jpg');\n\t\t\t$this->email->attach('/path/to/photo2.jpg');\n\t\t\t$this->email->attach('/path/to/photo3.jpg');\n\n\t\tTo use the default disposition (attachment), leave the second parameter blank,\n\t\totherwise use a custom disposition::\n\n\t\t\t$this->email->attach('image.jpg', 'inline');\n\n\t\tYou can also use a URL::\n\n\t\t\t$this->email->attach('http://example.com/filename.pdf');\n\n\t\tIf you'd like to use a custom file name, you can use the third parameter::\n\n\t\t\t$this->email->attach('filename.pdf', 'attachment', 'report.pdf');\n\n\t\tIf you need to use a buffer string instead of a real - physical - file you can\n\t\tuse the first parameter as buffer, the third parameter as file name and the fourth\n\t\tparameter as mime-type::\n\n\t\t\t$this->email->attach($buffer, 'attachment', 'report.pdf', 'application/pdf');\n\n\t.. php:method:: attachment_cid($filename)\n\n\t\t:param\tstring\t$filename: Existing attachment filename\n\t\t:returns:\tAttachment Content-ID or FALSE if not found\n\t\t:rtype:\tstring\n \n\t\tSets and returns an attachment's Content-ID, which enables your to embed an inline\n\t\t(picture) attachment into HTML. First parameter must be the already attached file name.\n\t\t::\n \n\t\t\t$filename = '/img/photo1.jpg';\n\t\t\t$this->email->attach($filename);\n\t\t\tforeach ($list as $address)\n\t\t\t{\n\t\t\t\t$this->email->to($address);\n\t\t\t\t$cid = $this->email->attachment_cid($filename);\n\t\t\t\t$this->email->message('<img src=\"cid:'. $cid .'\" alt=\"photo1\" />');\n\t\t\t\t$this->email->send();\n\t\t\t}\n\n\t\t.. note:: Content-ID for each e-mail must be re-created for it to be unique.\n\n\t.. php:method:: print_debugger([$include = array('headers', 'subject', 'body')])\n\n\t\t:param\tarray\t$include: Which parts of the message to print out\n\t\t:returns:\tFormatted debug data\n\t\t:rtype:\tstring\n\n\t\tReturns a string containing any server messages, the email headers, and\n\t\tthe email message. Useful for debugging.\n\n\t\tYou can optionally specify which parts of the message should be printed.\n\t\tValid options are: **headers**, **subject**, **body**.\n\n\t\tExample::\n\n\t\t\t// You need to pass FALSE while sending in order for the email data\n\t\t\t// to not be cleared - if that happens, print_debugger() would have\n\t\t\t// nothing to output.\n\t\t\t$this->email->send(FALSE);\n\n\t\t\t// Will only print the email headers, excluding the message subject and body\n\t\t\t$this->email->print_debugger(array('headers'));\n\n\t\t.. note:: By default, all of the raw data will be printed.\n"
  },
  {
    "path": "user_guide_src/source/libraries/encryption.rst",
    "content": "##################\nEncryption Library\n##################\n\n.. important:: DO NOT use this or any other *encryption* library for\n\tuser password storage! Passwords must be *hashed* instead, and you\n\tshould do that via PHP's own `Password Hashing extension\n\t<https://secure.php.net/password>`_.\n\nThe Encryption Library provides two-way data encryption. To do so in\na cryptographically secure way, it utilizes PHP extensions that are\nunfortunately not always available on all systems.\nYou must meet one of the following dependencies in order to use this\nlibrary:\n\n- `OpenSSL <https://secure.php.net/openssl>`_\n- `MCrypt <https://secure.php.net/mcrypt>`_ (and `MCRYPT_DEV_URANDOM` availability)\n\nIf neither of the above dependencies is met, we simply cannot offer\nyou a good enough implementation to meet the high standards required\nfor proper cryptography.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n****************************\nUsing the Encryption Library\n****************************\n\nInitializing the Class\n======================\n\nLike most other classes in CodeIgniter, the Encryption library is\ninitialized in your controller using the ``$this->load->library()``\nmethod::\n\n\t$this->load->library('encryption');\n\nOnce loaded, the Encryption library object will be available using::\n\n\t$this->encryption\n\nDefault behavior\n================\n\nBy default, the Encryption Library will use the AES-128 cipher in CBC\nmode, using your configured *encryption_key* and SHA512 HMAC authentication.\n\n.. note:: AES-128 is chosen both because it is proven to be strong and\n\tbecause of its wide availability across different cryptographic\n\tsoftware and programming languages' APIs.\n\nHowever, the *encryption_key* is not used as is.\n\nIf you are somewhat familiar with cryptography, you should already know\nthat a HMAC also requires a secret key and using the same key for both\nencryption and authentication is a bad practice.\n\nBecause of that, two separate keys are derived from your already configured\n*encryption_key*: one for encryption and one for authentication. This is\ndone via a technique called `HMAC-based Key Derivation Function\n<https://en.wikipedia.org/wiki/HKDF>`_ (HKDF).\n\nSetting your encryption_key\n===========================\n\nAn *encryption key* is a piece of information that controls the\ncryptographic process and permits a plain-text string to be encrypted,\nand afterwards - decrypted. It is the secret \"ingredient\" in the whole\nprocess that allows you to be the only one who is able to decrypt data\nthat you've decided to hide from the eyes of the public.\nAfter one key is used to encrypt data, that same key provides the **only**\nmeans to decrypt it, so not only must you chose one carefully, but you\nmust not lose it or you will also lose access to the data.\n\nIt must be noted that to ensure maximum security, such key *should* not\nonly be as strong as possible, but also often changed. Such behavior\nhowever is rarely practical or possible to implement, and that is why\nCodeIgniter gives you the ability to configure a single key that is to be\nused (almost) every time.\n\nIt goes without saying that you should guard your key carefully. Should\nsomeone gain access to your key, the data will be easily decrypted. If\nyour server is not totally under your control it's impossible to ensure\nkey security so you may want to think carefully before using it for\nanything that requires high security, like storing credit card numbers.\n\nYour encryption key **must** be as long as the encyption algorithm in use\nallows. For AES-128, that's 128 bits or 16 bytes (characters) long.\nYou will find a table below that shows the supported key lengths of\ndifferent ciphers.\n\nThe key should be as random as possible and it **must not** be a regular\ntext string, nor the output of a hashing function, etc. In order to create\na proper key, you must use the Encryption library's ``create_key()`` method\n::\n\n\t// $key will be assigned a 16-byte (128-bit) random key\n\t$key = $this->encryption->create_key(16);\n\nThe key can be either stored in your *application/config/config.php*, or\nyou can design your own storage mechanism and pass the key dynamically\nwhen encrypting/decrypting.\n\nTo save your key to your *application/config/config.php*, open the file\nand set::\n\n\t$config['encryption_key'] = 'YOUR KEY';\n\nYou'll notice that the ``create_key()`` method outputs binary data, which\nis hard to deal with (i.e. a copy-paste may damage it), so you may use\n``bin2hex()``, ``hex2bin()`` or Base64-encoding to work with the key in\na more friendly manner. For example::\n\n\t// Get a hex-encoded representation of the key:\n\t$key = bin2hex($this->encryption->create_key(16));\n\n\t// Put the same value in your config with hex2bin(),\n\t// so that it is still passed as binary to the library:\n\t$config['encryption_key'] = hex2bin(<your hex-encoded key>);\n\n.. _ciphers-and-modes:\n\nSupported encryption ciphers and modes\n======================================\n\n.. note:: The terms 'cipher' and 'encryption algorithm' are interchangeable.\n\nPortable ciphers\n----------------\n\nBecause MCrypt and OpenSSL (also called drivers throughout this document)\neach support different sets of encryption algorithms and often implement\nthem in different ways, our Encryption library is designed to use them in\na portable fashion, or in other words - it enables you to use them\ninterchangeably, at least for the ciphers supported by both drivers.\n\nIt is also implemented in a way that aims to match the standard\nimplementations in other programming languages and libraries.\n\nHere's a list of the so called \"portable\" ciphers, where\n\"CodeIgniter name\" is the string value that you'd have to pass to the\nEncryption library to use that cipher:\n\n======================== ================== ============================ ===============================\nCipher name              CodeIgniter name   Key lengths (bits / bytes)   Supported modes\n======================== ================== ============================ ===============================\nAES-128 / Rijndael-128   aes-128            128 / 16                     CBC, CTR, CFB, CFB8, OFB, ECB\nAES-192                  aes-192            192 / 24                     CBC, CTR, CFB, CFB8, OFB, ECB\nAES-256                  aes-256            256 / 32                     CBC, CTR, CFB, CFB8, OFB, ECB\nDES                      des                56 / 7                       CBC, CFB, CFB8, OFB, ECB\nTripleDES                tripledes          56 / 7, 112 / 14, 168 / 21   CBC, CFB, CFB8, OFB\nBlowfish                 blowfish           128-448 / 16-56              CBC, CFB, OFB, ECB\nCAST5 / CAST-128         cast5              88-128 / 11-16               CBC, CFB, OFB, ECB\nRC4 / ARCFour            rc4                40-2048 / 5-256              Stream\n======================== ================== ============================ ===============================\n\n.. important:: Because of how MCrypt works, if you fail to provide a key\n\twith the appropriate length, you might end up using a different\n\talgorithm than the one configured, so be really careful with that!\n\n.. note:: In case it isn't clear from the above table, Blowfish, CAST5\n\tand RC4 support variable length keys. That is, any number in the\n\tshown ranges is valid, although in bit terms that only happens\n\tin 8-bit increments.\n\n.. note:: Even though CAST5 supports key lengths lower than 128 bits\n\t(16 bytes), in fact they will just be zero-padded to the\n\tmaximum length, as specified in `RFC 2144\n\t<https://tools.ietf.org/rfc/rfc2144.txt>`_.\n\n.. note:: Blowfish supports key lengths as small as 32 bits (4 bytes), but\n\tour tests have shown that only lengths of 128 bits (16 bytes) or\n\thigher are properly supported by both MCrypt and OpenSSL. It is\n\talso a bad practice to use such low-length keys anyway.\n\nDriver-specific ciphers\n-----------------------\n\nAs noted above, MCrypt and OpenSSL support different sets of encryption\nciphers. For portability reasons and because we haven't tested them\nproperly, we do not advise you to use the ones that are driver-specific,\nbut regardless, here's a list of most of them:\n\n\n============== ========= ============================== =========================================\nCipher name    Driver    Key lengths (bits / bytes)     Supported modes\n============== ========= ============================== =========================================\nAES-128        OpenSSL   128 / 16                       CBC, CTR, CFB, CFB8, OFB, ECB, XTS\nAES-192        OpenSSL   192 / 24                       CBC, CTR, CFB, CFB8, OFB, ECB, XTS\nAES-256        OpenSSL   256 / 32                       CBC, CTR, CFB, CFB8, OFB, ECB, XTS\nRijndael-128   MCrypt    128 / 16, 192 / 24, 256 / 32   CBC, CTR, CFB, CFB8, OFB, OFB8, ECB\nRijndael-192   MCrypt    128 / 16, 192 / 24, 256 / 32   CBC, CTR, CFB, CFB8, OFB, OFB8, ECB\nRijndael-256   MCrypt    128 / 16, 192 / 24, 256 / 32   CBC, CTR, CFB, CFB8, OFB, OFB8, ECB\nGOST           MCrypt    256 / 32                       CBC, CTR, CFB, CFB8, OFB, OFB8, ECB\nTwofish        MCrypt    128 / 16, 192 / 24, 256 / 32   CBC, CTR, CFB, CFB8, OFB, OFB8, ECB\nCAST-128       MCrypt    40-128 / 5-16                  CBC, CTR, CFB, CFB8, OFB, OFB8, ECB\nCAST-256       MCrypt    128 / 16, 192 / 24, 256 / 32   CBC, CTR, CFB, CFB8, OFB, OFB8, ECB\nLoki97         MCrypt    128 / 16, 192 / 24, 256 / 32   CBC, CTR, CFB, CFB8, OFB, OFB8, ECB\nSaferPlus      MCrypt    128 / 16, 192 / 24, 256 / 32   CBC, CTR, CFB, CFB8, OFB, OFB8, ECB\nSerpent        MCrypt    128 / 16, 192 / 24, 256 / 32   CBC, CTR, CFB, CFB8, OFB, OFB8, ECB\nXTEA           MCrypt    128 / 16                       CBC, CTR, CFB, CFB8, OFB, OFB8, ECB\nRC2            MCrypt    8-1024 / 1-128                 CBC, CTR, CFB, CFB8, OFB, OFB8, ECB\nRC2            OpenSSL   8-1024 / 1-128                 CBC, CFB, OFB, ECB\nCamellia-128   OpenSSL   128 / 16                       CBC, CFB, CFB8, OFB, ECB\nCamellia-192   OpenSSL   192 / 24                       CBC, CFB, CFB8, OFB, ECB\nCamellia-256   OpenSSL   256 / 32                       CBC, CFB, CFB8, OFB, ECB\nSeed           OpenSSL   128 / 16                       CBC, CFB, OFB, ECB\n============== ========= ============================== =========================================\n\n.. note:: If you wish to use one of those ciphers, you'd have to pass\n\tits name in lower-case to the Encryption library.\n\n.. note:: You've probably noticed that all AES cipers (and Rijndael-128)\n\tare also listed in the portable ciphers list. This is because\n\tdrivers support different modes for these ciphers. Also, it is\n\timportant to note that AES-128 and Rijndael-128 are actually\n\tthe same cipher, but **only** when used with a 128-bit key.\n\n.. note:: CAST-128 / CAST-5 is also listed in both the portable and\n\tdriver-specific ciphers list. This is because OpenSSL's\n\timplementation doesn't appear to be working correctly with\n\tkey sizes of 80 bits and lower.\n\n.. note:: RC2 is listed as supported by both MCrypt and OpenSSL.\n\tHowever, both drivers implement them differently and they\n\tare not portable. It is probably worth noting that we only\n\tfound one obscure source confirming that it is MCrypt that\n\tis not properly implementing it.\n\n.. _encryption-modes:\n\nEncryption modes\n----------------\n\nDifferent modes of encryption have different characteristics and serve\nfor different purposes. Some are stronger than others, some are faster\nand some offer extra features.\nWe are not going in depth into that here, we'll leave that to the\ncryptography experts. The table below is to provide brief informational\nreference to our more experienced users. If you are a beginner, just\nstick to the CBC mode - it is widely accepted as strong and secure for\ngeneral purposes.\n\n=========== ================== ================= ===================================================================================================================================================\nMode name   CodeIgniter name   Driver support    Additional info\n=========== ================== ================= ===================================================================================================================================================\nCBC         cbc                MCrypt, OpenSSL   A safe default choice\nCTR         ctr                MCrypt, OpenSSL   Considered as theoretically better than CBC, but not as widely available\nCFB         cfb                MCrypt, OpenSSL   N/A\nCFB8        cfb8               MCrypt, OpenSSL   Same as CFB, but operates in 8-bit mode (not recommended).\nOFB         ofb                MCrypt, OpenSSL   N/A\nOFB8        ofb8               MCrypt            Same as OFB, but operates in 8-bit mode (not recommended).\nECB         ecb                MCrypt, OpenSSL   Ignores IV (not recommended).\nXTS         xts                OpenSSL           Usually used for encrypting random access data such as RAM or hard-disk storage.\nStream      stream             MCrypt, OpenSSL   This is not actually a mode, it just says that a stream cipher is being used. Required because of the general cipher+mode initialization process.\n=========== ================== ================= ===================================================================================================================================================\n\nMessage Length\n==============\n\nIt's probably important for you to know that an encrypted string is usually\nlonger than the original, plain-text string (depending on the cipher).\n\nThis is influenced by the cipher algorithm itself, the IV prepended to the\ncipher-text and the HMAC authentication message that is also prepended.\nFurthermore, the encrypted message is also Base64-encoded so that it is safe\nfor storage and transmission, regardless of a possible character set in use.\n\nKeep this information in mind when selecting your data storage mechanism.\nCookies, for example, can only hold 4K of information.\n\n.. _configuration:\n\nConfiguring the library\n=======================\n\nFor usability, performance, but also historical reasons tied to our old\n**Encrypt Class**, the Encryption library is designed to use repeatedly\nthe same driver, encryption cipher, mode and key.\n\nAs noted in the \"Default behavior\" section above, this means using an\nauto-detected driver (OpenSSL has a higher priority), the AES-128 ciper\nin CBC mode, and your ``$config['encryption_key']`` value.\n\nIf you wish to change that however, you need to use the ``initialize()``\nmethod. It accepts an associative array of parameters, all of which are\noptional:\n\n======== ===============================================\nOption   Possible values\n======== ===============================================\ndriver   'mcrypt', 'openssl'\ncipher   Cipher name (see :ref:`ciphers-and-modes`)\nmode     Encryption mode (see :ref:`encryption-modes`)\nkey      Encryption key \n======== ===============================================\n\nFor example, if you were to change the encryption algorithm and\nmode to AES-256 in CTR mode, this is what you should do::\n\n\t$this->encryption->initialize(\n\t\tarray(\n\t\t\t'cipher' => 'aes-256',\n\t\t\t'mode' => 'ctr',\n\t\t\t'key' => '<a 32-character random string>'\n\t\t)\n\t);\n\nNote that we only mentioned that you want to change the ciper and mode,\nbut we also included a key in the example. As previously noted, it is\nimportant that you choose a key with a proper size for the used algorithm.\n\nThere's also the ability to change the driver, if for some reason you\nhave both, but want to use MCrypt instead of OpenSSL::\n\n\t// Switch to the MCrypt driver\n\t$this->encryption->initialize(array('driver' => 'mcrypt'));\n\n\t// Switch back to the OpenSSL driver\n\t$this->encryption->initialize(array('driver' => 'openssl'));\n\nEncrypting and decrypting data\n==============================\n\nEncrypting and decrypting data with the already configured library\nsettings is simple. As simple as just passing the string to the\n``encrypt()`` and/or ``decrypt()`` methods::\n\n\t$plain_text = 'This is a plain-text message!';\n\t$ciphertext = $this->encryption->encrypt($plain_text);\n\n\t// Outputs: This is a plain-text message!\n\techo $this->encryption->decrypt($ciphertext);\n\nAnd that's it! The Encryption library will do everything necessary\nfor the whole process to be cryptographically secure out-of-the-box.\nYou don't need to worry about it.\n\n.. important:: Both methods will return FALSE in case of an error.\n\tWhile for ``encrypt()`` this can only mean incorrect\n\tconfiguration, you should always check the return value\n\tof ``decrypt()`` in production code.\n\nHow it works\n------------\n\nIf you must know how the process works, here's what happens under\nthe hood:\n\n- ``$this->encryption->encrypt($plain_text)``\n\n  #. Derive an encryption key and a HMAC key from your configured\n     *encryption_key* via HKDF, using the SHA-512 digest algorithm.\n\n  #. Generate a random initialization vector (IV).\n\n  #. Encrypt the data via AES-128 in CBC mode (or another previously\n     configured cipher and mode), using the above-mentioned derived\n     encryption key and IV.\n\n  #. Prepend said IV to the resulting cipher-text.\n\n  #. Base64-encode the resulting string, so that it can be safely\n     stored or transferred without worrying about character sets.\n\n  #. Create a SHA-512 HMAC authentication message using the derived\n     HMAC key to ensure data integrity and prepend it to the Base64\n     string.\n\n- ``$this->encryption->decrypt($ciphertext)``\n\n  #. Derive an encryption key and a HMAC key from your configured\n     *encryption_key* via HKDF, using the SHA-512 digest algorithm.\n     Because your configured *encryption_key* is the same, this\n     will produce the same result as in the ``encrypt()`` method\n     above - otherwise you won't be able to decrypt it.\n\n  #. Check if the string is long enough, separate the HMAC out of\n     it and validate if it is correct (this is done in a way that\n     prevents timing attacks against it). Return FALSE if either of\n     the checks fails.\n\n  #. Base64-decode the string.\n\n  #. Separate the IV out of the cipher-text and decrypt the said\n     cipher-text using that IV and the derived encryption key.\n\n.. _custom-parameters:\n\nUsing custom parameters\n-----------------------\n\nLet's say you have to interact with another system that is out\nof your control and uses another method to encrypt data. A\nmethod that will most certainly not match the above-described\nsequence and probably not use all of the steps either.\n\nThe Encryption library allows you to change how its encryption\nand decryption processes work, so that you can easily tailor a\ncustom solution for such situations.\n\n.. note:: It is possible to use the library in this way, without\n\tsetting an *encryption_key* in your configuration file.\n\nAll you have to do is to pass an associative array with a few\nparameters to either the ``encrypt()`` or ``decrypt()`` method.\nHere's an example::\n\n\t// Assume that we have $ciphertext, $key and $hmac_key\n\t// from on outside source\n\n\t$message = $this->encryption->decrypt(\n\t\t$ciphertext,\n\t\tarray(\n\t\t\t'cipher' => 'blowfish',\n\t\t\t'mode' => 'cbc',\n\t\t\t'key' => $key,\n\t\t\t'hmac_digest' => 'sha256',\n\t\t\t'hmac_key' => $hmac_key\n\t\t)\n\t);\n\nIn the above example, we are decrypting a message that was encrypted\nusing the Blowfish cipher in CBC mode and authenticated via a SHA-256\nHMAC.\n\n.. important:: Note that both 'key' and 'hmac_key' are used in this\n\texample. When using custom parameters, encryption and HMAC keys\n\tare not derived like the default behavior of the library is.\n\nBelow is a list of the available options.\n\nHowever, unless you really need to and you know what you are doing,\nwe advise you to not change the encryption process as this could\nimpact security, so please do so with caution.\n\n============= =============== ============================= ======================================================\nOption        Default value   Mandatory / Optional          Description\n============= =============== ============================= ======================================================\ncipher        N/A             Yes                           Encryption algorithm (see :ref:`ciphers-and-modes`).\nmode          N/A             Yes                           Encryption mode (see :ref:`encryption-modes`).\nkey           N/A             Yes                           Encryption key.\nhmac          TRUE            No                            Whether to use a HMAC.\n                                                            Boolean. If set to FALSE, then *hmac_digest* and\n                                                            *hmac_key* will be ignored.\nhmac_digest   sha512          No                            HMAC message digest algorithm (see :ref:`digests`).\nhmac_key      N/A             Yes, unless *hmac* is FALSE   HMAC key.\nraw_data      FALSE           No                            Whether the cipher-text should be raw.\n                                                            Boolean. If set to TRUE, then Base64 encoding and\n                                                            decoding will not be performed and HMAC will not\n                                                            be a hexadecimal string.\n============= =============== ============================= ======================================================\n\n.. important:: ``encrypt()`` and ``decrypt()`` will return FALSE if\n\ta mandatory parameter is not provided or if a provided\n\tvalue is incorrect. This includes *hmac_key*, unless *hmac*\n\tis set to FALSE.\n\n.. _digests:\n\nSupported HMAC authentication algorithms\n----------------------------------------\n\nFor HMAC message authentication, the Encryption library supports\nusage of the SHA-2 family of algorithms:\n\n=========== ==================== ============================\nAlgorithm   Raw length (bytes)   Hex-encoded length (bytes)\n=========== ==================== ============================\nsha512      64                   128\nsha384      48                   96\nsha256      32                   64\nsha224      28                   56\n=========== ==================== ============================\n\nThe reason for not including other popular algorithms, such as\nMD5 or SHA1 is that they are no longer considered secure enough\nand as such, we don't want to encourage their usage.\nIf you absolutely need to use them, it is easy to do so via PHP's\nnative `hash_hmac() <https://secure.php.net/manual/en/function.hash-hmac.php>`_ function.\n\nStronger algorithms of course will be added in the future as they\nappear and become widely available.\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Encryption\n\n\t.. php:method:: initialize($params)\n\n\t\t:param\tarray\t$params: Configuration parameters\n\t\t:returns:\tCI_Encryption instance (method chaining)\n\t\t:rtype:\tCI_Encryption\n\n\t\tInitializes (configures) the library to use a different\n\t\tdriver, cipher, mode or key.\n\n\t\tExample::\n\n\t\t\t$this->encryption->initialize(\n\t\t\t\tarray('mode' => 'ctr')\n\t\t\t);\n\n\t\tPlease refer to the :ref:`configuration` section for detailed info.\n\n\t.. php:method:: encrypt($data[, $params = NULL])\n\n\t\t:param\tstring\t$data: Data to encrypt\n\t\t:param\tarray\t$params: Optional parameters\n\t\t:returns:\tEncrypted data or FALSE on failure\n\t\t:rtype:\tstring\n\n\t\tEncrypts the input data and returns its ciphertext.\n\n\t\tExample::\n\n\t\t\t$ciphertext = $this->encryption->encrypt('My secret message');\n\n\t\tPlease refer to the :ref:`custom-parameters` section for information\n\t\ton the optional parameters.\n\n\t.. php:method:: decrypt($data[, $params = NULL])\n\n\t\t:param\tstring\t$data: Data to decrypt\n\t\t:param\tarray\t$params: Optional parameters\n\t\t:returns:\tDecrypted data or FALSE on failure\n\t\t:rtype:\tstring\n\n\t\tDecrypts the input data and returns it in plain-text.\n\n\t\tExample::\n\n\t\t\techo $this->encryption->decrypt($ciphertext);\n\n\t\tPlease refer to the :ref:`custom-parameters` secrion for information\n\t\ton the optional parameters.\n\n\t.. php:method:: create_key($length)\n\n\t\t:param\tint\t$length: Output length\n\t\t:returns:\tA pseudo-random cryptographic key with the specified length, or FALSE on failure\n\t\t:rtype:\tstring\n\n\t\tCreates a cryptographic key by fetching random data from\n\t\tthe operating system's sources (i.e. /dev/urandom).\n\n\t.. php:method:: hkdf($key[, $digest = 'sha512'[, $salt = NULL[, $length = NULL[, $info = '']]]])\n\n\t\t:param\tstring\t$key: Input key material\n\t\t:param\tstring\t$digest: A SHA-2 family digest algorithm\n\t\t:param\tstring\t$salt: Optional salt\n\t\t:param\tint\t$length: Optional output length\n\t\t:param\tstring\t$info: Optional context/application-specific info\n\t\t:returns:\tA pseudo-random key or FALSE on failure\n\t\t:rtype:\tstring\n\n\t\tDerives a key from another, presumably weaker key.\n\n\t\tThis method is used internally to derive an encryption and HMAC key\n\t\tfrom your configured *encryption_key*.\n\n\t\tIt is publicly available due to its otherwise general purpose. It is\n\t\tdescribed in `RFC 5869 <https://tools.ietf.org/rfc/rfc5869.txt>`_.\n\n\t\tHowever, as opposed to the description in RFC 5869, this implementation\n\t\tdoesn't support SHA1.\n\n\t\tExample::\n\n\t\t\t$hmac_key = $this->encryption->hkdf(\n\t\t\t\t$key,\n\t\t\t\t'sha512',\n\t\t\t\tNULL,\n\t\t\t\tNULL,\n\t\t\t\t'authentication'\n\t\t\t);\n\n\t\t\t// $hmac_key is a pseudo-random key with a length of 64 bytes\n"
  },
  {
    "path": "user_guide_src/source/libraries/file_uploading.rst",
    "content": "####################\nFile Uploading Class\n####################\n\nCodeIgniter's File Uploading Class permits files to be uploaded. You can\nset various preferences, restricting the type and size of the files.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n***********\nThe Process\n***********\n\nUploading a file involves the following general process:\n\n-  An upload form is displayed, allowing a user to select a file and\n   upload it.\n-  When the form is submitted, the file is uploaded to the destination\n   you specify.\n-  Along the way, the file is validated to make sure it is allowed to be\n   uploaded based on the preferences you set.\n-  Once uploaded, the user will be shown a success message.\n\nTo demonstrate this process here is brief tutorial. Afterward you'll\nfind reference information.\n\nCreating the Upload Form\n========================\n\nUsing a text editor, create a form called upload_form.php. In it, place\nthis code and save it to your **application/views/** directory::\n\n\t<html lang=\"en\">\n\t<head>\n\t<title>Upload Form</title>\n\t</head>\n\t<body>\n\n\t<?php echo $error;?>\n\n\t<?php echo form_open_multipart('upload/do_upload');?>\n\n\t<input type=\"file\" name=\"userfile\" size=\"20\" />\n\n\t<br /><br />\n\n\t<input type=\"submit\" value=\"upload\" />\n\n\t</form>\n\n\t</body>\n\t</html>\n\nYou'll notice we are using a form helper to create the opening form tag.\nFile uploads require a multipart form, so the helper creates the proper\nsyntax for you. You'll also notice we have an $error variable. This is\nso we can show error messages in the event the user does something\nwrong.\n\nThe Success Page\n================\n\nUsing a text editor, create a form called upload_success.php. In it,\nplace this code and save it to your **application/views/** directory::\n\n\t<html lang=\"en\">\n\t<head>\n\t<title>Upload Form</title>\n\t</head>\n\t<body>\n\n\t<h3>Your file was successfully uploaded!</h3>\n\n\t<ul>\n\t<?php foreach ($upload_data as $item => $value):?>\n\t<li><?php echo $item;?>: <?php echo $value;?></li>\n\t<?php endforeach; ?>\n\t</ul>\n\n\t<p><?php echo anchor('upload', 'Upload Another File!'); ?></p>\n\n\t</body>\n\t</html>\n\nThe Controller\n==============\n\nUsing a text editor, create a controller called Upload.php. In it, place\nthis code and save it to your **application/controllers/** directory::\n\n\t<?php\n\n\tclass Upload extends CI_Controller {\n\n\t\tpublic function __construct()\n\t\t{\n\t\t\tparent::__construct();\n\t\t\t$this->load->helper(array('form', 'url'));\n\t\t}\n\n\t\tpublic function index()\n\t\t{\n\t\t\t$this->load->view('upload_form', array('error' => ' ' ));\n\t\t}\n\n\t\tpublic function do_upload()\n\t\t{\n\t\t\t$config['upload_path']\t\t= './uploads/';\n\t\t\t$config['allowed_types']\t= 'gif|jpg|png';\n\t\t\t$config['max_size']\t\t= 100;\n\t\t\t$config['max_width']\t\t= 1024;\n\t\t\t$config['max_height']\t\t= 768;\n\n\t\t\t$this->load->library('upload', $config);\n\n\t\t\tif ( ! $this->upload->do_upload('userfile'))\n\t\t\t{\n\t\t\t\t$error = array('error' => $this->upload->display_errors());\n\n\t\t\t\t$this->load->view('upload_form', $error);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$data = array('upload_data' => $this->upload->data());\n\n\t\t\t\t$this->load->view('upload_success', $data);\n\t\t\t}\n\t\t}\n\t}\n\t?>\n\nThe Upload Directory\n====================\n\nYou'll need a destination directory for your uploaded images. Create a\ndirectory at the root of your CodeIgniter installation called uploads\nand set its file permissions to 777.\n\nTry it!\n=======\n\nTo try your form, visit your site using a URL similar to this one::\n\n\texample.com/index.php/upload/\n\nYou should see an upload form. Try uploading an image file (either a\njpg, gif, or png). If the path in your controller is correct it should\nwork.\n\n***************\nReference Guide\n***************\n\nInitializing the Upload Class\n=============================\n\nLike most other classes in CodeIgniter, the Upload class is initialized\nin your controller using the ``$this->load->library()`` method::\n\n\t$this->load->library('upload');\n\nOnce the Upload class is loaded, the object will be available using:\n$this->upload\n\nSetting Preferences\n===================\n\nSimilar to other libraries, you'll control what is allowed to be upload\nbased on your preferences. In the controller you built above you set the\nfollowing preferences::\n\n\t$config['upload_path'] = './uploads/';\n\t$config['allowed_types'] = 'gif|jpg|png';\n\t$config['max_size']\t= '100';\n\t$config['max_width'] = '1024';\n\t$config['max_height'] = '768';\n\n\t$this->load->library('upload', $config);\n\n\t// Alternately you can set preferences by calling the ``initialize()`` method. Useful if you auto-load the class:\n\t$this->upload->initialize($config);\n\nThe above preferences should be fairly self-explanatory. Below is a\ntable describing all available preferences.\n\nPreferences\n===========\n\nThe following preferences are available. The default value indicates\nwhat will be used if you do not specify that preference.\n\n============================ ================= ======================= ======================================================================\nPreference                   Default Value     Options                 Description\n============================ ================= ======================= ======================================================================\n**upload_path**              None              None                    The path to the directory where the upload should be placed. The\n                                                                       directory must be writable and the path can be absolute or relative.\n**allowed_types**            None              None                    The mime types corresponding to the types of files you allow to be\n                                                                       uploaded. Usually the file extension can be used as the mime type.\n                                                                       Can be either an array or a pipe-separated string.\n**file_name**                None              Desired file name       If set CodeIgniter will rename the uploaded file to this name. The\n                                                                       extension provided in the file name must also be an allowed file type.\n                                                                       If no extension is provided in the original file_name will be used.\n**file_ext_tolower**         FALSE             TRUE/FALSE (boolean)    If set to TRUE, the file extension will be forced to lower case\n**overwrite**                FALSE             TRUE/FALSE (boolean)    If set to true, if a file with the same name as the one you are\n                                                                       uploading exists, it will be overwritten. If set to false, a number will\n                                                                       be appended to the filename if another with the same name exists.\n**max_size**                 0                 None                    The maximum size (in kilobytes) that the file can be. Set to zero for no\n                                                                       limit. Note: Most PHP installations have their own limit, as specified\n                                                                       in the php.ini file. Usually 2 MB (or 2048 KB) by default.\n**max_width**                0                 None                    The maximum width (in pixels) that the image can be. Set to zero for no\n                                                                       limit.\n**max_height**               0                 None                    The maximum height (in pixels) that the image can be. Set to zero for no\n                                                                       limit.\n**min_width**                0                 None                    The minimum width (in pixels) that the image can be. Set to zero for no\n                                                                       limit.\n**min_height**               0                 None                    The minimum height (in pixels) that the image can be. Set to zero for no\n                                                                       limit.\n**max_filename**             0                 None                    The maximum length that a file name can be. Set to zero for no limit.\n**max_filename_increment**   100               None                    When overwrite is set to FALSE, use this to set the maximum filename\n                                                                       increment for CodeIgniter to append to the filename.\n**encrypt_name**             FALSE             TRUE/FALSE (boolean)    If set to TRUE the file name will be converted to a random encrypted\n                                                                       string. This can be useful if you would like the file saved with a name\n                                                                       that can not be discerned by the person uploading it.\n**remove_spaces**            TRUE              TRUE/FALSE (boolean)    If set to TRUE, any spaces in the file name will be converted to\n                                                                       underscores. This is recommended.\n**detect_mime**              TRUE              TRUE/FALSE (boolean)    If set to TRUE, a server side detection of the file type will be\n                                                                       performed to avoid code injection attacks. DO NOT disable this option\n                                                                       unless you have no other option as that would cause a security risk.\n**mod_mime_fix**             TRUE              TRUE/FALSE (boolean)    If set to TRUE, multiple filename extensions will be suffixed with an\n                                                                       underscore in order to avoid triggering `Apache mod_mime\n                                                                       <https://httpd.apache.org/docs/2.0/mod/mod_mime.html#multipleext>`_.\n                                                                       DO NOT turn off this option if your upload directory is public, as this\n                                                                       is a security risk.\n============================ ================= ======================= ======================================================================\n\nSetting preferences in a config file\n====================================\n\nIf you prefer not to set preferences using the above method, you can\ninstead put them into a config file. Simply create a new file called the\nupload.php, add the $config array in that file. Then save the file in:\n**config/upload.php** and it will be used automatically. You will NOT\nneed to use the ``$this->upload->initialize()`` method if you save your\npreferences in a config file.\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Upload\n\n\t.. php:method:: initialize([array $config = array()[, $reset = TRUE]])\n\n\t\t:param\tarray\t$config: Preferences\n\t\t:param\tbool\t$reset: Whether to reset preferences (that are not provided in $config) to their defaults\n\t\t:returns:\tCI_Upload instance (method chaining)\n\t\t:rtype:\tCI_Upload\n\n\t.. php:method:: do_upload([$field = 'userfile'])\n\n\t\t:param\tstring\t$field: Name of the form field\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tPerforms the upload based on the preferences you've set.\n\n\t\t.. note:: By default the upload routine expects the file to come from\n\t\t\ta form field called userfile, and the form must be of type\n\t\t\t\"multipart\".\n\n\t\t::\n\n\t\t\t<form method=\"post\" action=\"some_action\" enctype=\"multipart/form-data\" />\n\n\t\tIf you would like to set your own field name simply pass its value to\n\t\tthe ``do_upload()`` method::\n\n\t\t\t$field_name = \"some_field_name\";\n\t\t\t$this->upload->do_upload($field_name);\n\n\t.. php:method:: display_errors([$open = '<p>'[, $close = '</p>']])\n\n\t\t:param\tstring\t$open: Opening markup\n\t\t:param\tstring\t$close: Closing markup\n\t\t:returns:\tFormatted error message(s)\n\t\t:rtype:\tstring\n\n\t\tRetrieves any error messages if the ``do_upload()`` method returned\n\t\tfalse. The method does not echo automatically, it returns the data so\n\t\tyou can assign it however you need.\n\n\t\t**Formatting Errors**\n\n\t\t\tBy default the above method wraps any errors within <p> tags. You can\n\t\t\tset your own delimiters like this::\n\n\t\t\t\t$this->upload->display_errors('<p>', '</p>');\n\n\n\t.. php:method:: data([$index = NULL])\n\n\t\t:param\tstring\t$data: Element to return instead of the full array\n\t\t:returns:\tInformation about the uploaded file\n\t\t:rtype:\tmixed\n\n\t\tThis is a helper method that returns an array containing all of the\n\t\tdata related to the file you uploaded. Here is the array prototype::\n\n\t\t\tArray\n\t\t\t(\n\t\t\t\t[file_name]\t=> mypic.jpg\n\t\t\t\t[file_type]\t=> image/jpeg\n\t\t\t\t[file_path]\t=> /path/to/your/upload/\n\t\t\t\t[full_path]\t=> /path/to/your/upload/jpg.jpg\n\t\t\t\t[raw_name]\t=> mypic\n\t\t\t\t[orig_name]\t=> mypic.jpg\n\t\t\t\t[client_name]\t=> mypic.jpg\n\t\t\t\t[file_ext]\t=> .jpg\n\t\t\t\t[file_size]\t=> 22.2\n\t\t\t\t[is_image]\t=> 1\n\t\t\t\t[image_width]\t=> 800\n\t\t\t\t[image_height]\t=> 600\n\t\t\t\t[image_type]\t=> jpeg\n\t\t\t\t[image_size_str] => width=\"800\" height=\"200\"\n\t\t\t)\n\n\t\tTo return one element from the array::\n\n\t\t\t$this->upload->data('file_name');\t// Returns: mypic.jpg\n\n\t\tHere's a table explaining the above-displayed array items:\n\n\t\t================ ====================================================================================================\n\t\tItem             Description\n\t\t================ ====================================================================================================\n\t\tfile_name        Name of the file that was uploaded, including the filename extension\n\t\tfile_type        File MIME type identifier\n\t\tfile_path        Absolute server path to the file\n\t\tfull_path        Absolute server path, including the file name\n\t\traw_name         File name, without the extension\n\t\torig_name        Original file name. This is only useful if you use the encrypted name option.\n\t\tclient_name      File name supplied by the client user agent, but possibly sanitized\n\t\tfile_ext         Filename extension, period included\n\t\tfile_size        File size in kilobytes\n\t\tis_image         Whether the file is an image or not. 1 = image. 0 = not.\n\t\timage_width      Image width\n\t\timage_height     Image height\n\t\timage_type       Image type (usually the file name extension without the period)\n\t\timage_size_str   A string containing the width and height (useful to put into an image tag)\n\t\t================ ====================================================================================================\n"
  },
  {
    "path": "user_guide_src/source/libraries/form_validation.rst",
    "content": "###############\nForm Validation\n###############\n\nCodeIgniter provides a comprehensive form validation and data prepping\nclass that helps minimize the amount of code you'll write.\n\n.. contents:: Page Contents\n\n********\nOverview\n********\n\nBefore explaining CodeIgniter's approach to data validation, let's\ndescribe the ideal scenario:\n\n#. A form is displayed.\n#. You fill it in and submit it.\n#. If you submitted something invalid, or perhaps missed a required\n   item, the form is redisplayed containing your data along with an\n   error message describing the problem.\n#. This process continues until you have submitted a valid form.\n\nOn the receiving end, the script must:\n\n#. Check for required data.\n#. Verify that the data is of the correct type, and meets the correct\n   criteria. For example, if a username is submitted it must be\n   validated to contain only permitted characters. It must be of a\n   minimum length, and not exceed a maximum length. The username can't\n   be someone else's existing username, or perhaps even a reserved word.\n   Etc.\n#. Sanitize the data for security.\n#. Pre-format the data if needed (Does the data need to be trimmed? HTML\n   encoded? Etc.)\n#. Prep the data for insertion in the database.\n\nAlthough there is nothing terribly complex about the above process, it\nusually requires a significant amount of code, and to display error\nmessages, various control structures are usually placed within the form\nHTML. Form validation, while simple to create, is generally very messy\nand tedious to implement.\n\n************************\nForm Validation Tutorial\n************************\n\nWhat follows is a \"hands on\" tutorial for implementing CodeIgniter's Form\nValidation.\n\nIn order to implement form validation you'll need three things:\n\n#. A :doc:`View <../general/views>` file containing a form.\n#. A View file containing a \"success\" message to be displayed upon\n   successful submission.\n#. A :doc:`controller <../general/controllers>` method to receive and\n   process the submitted data.\n\nLet's create those three things, using a member sign-up form as the\nexample.\n\nThe Form\n========\n\nUsing a text editor, create a form called myform.php. In it, place this\ncode and save it to your application/views/ folder::\n\n\t<html lang=\"en\">\n\t<head>\n\t<title>My Form</title>\n\t</head>\n\t<body>\n\n\t<?php echo validation_errors(); ?>\n\n\t<?php echo form_open('form'); ?>\n\n\t<h5>Username</h5>\n\t<input type=\"text\" name=\"username\" value=\"\" size=\"50\" />\n\n\t<h5>Password</h5>\n\t<input type=\"text\" name=\"password\" value=\"\" size=\"50\" />\n\n\t<h5>Password Confirm</h5>\n\t<input type=\"text\" name=\"passconf\" value=\"\" size=\"50\" />\n\n\t<h5>Email Address</h5>\n\t<input type=\"text\" name=\"email\" value=\"\" size=\"50\" />\n\n\t<div><input type=\"submit\" value=\"Submit\" /></div>\n\n\t</form>\n\n\t</body>\n\t</html>\n\nThe Success Page\n================\n\nUsing a text editor, create a form called formsuccess.php. In it, place\nthis code and save it to your application/views/ folder::\n\n\t<html lang=\"en\">\n\t<head>\n\t<title>My Form</title>\n\t</head>\n\t<body>\n\n\t<h3>Your form was successfully submitted!</h3>\n\n\t<p><?php echo anchor('form', 'Try it again!'); ?></p>\n\n\t</body>\n\t</html>\n\nThe Controller\n==============\n\nUsing a text editor, create a controller called Form.php. In it, place\nthis code and save it to your application/controllers/ folder::\n\n\t<?php\n\n\tclass Form extends CI_Controller {\n\n\t\tpublic function index()\n\t\t{\n\t\t\t$this->load->helper(array('form', 'url'));\n\n\t\t\t$this->load->library('form_validation');\n\n\t\t\tif ($this->form_validation->run() == FALSE)\n\t\t\t{\n\t\t\t\t$this->load->view('myform');\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$this->load->view('formsuccess');\n\t\t\t}\n\t\t}\n\t}\n\nTry it!\n=======\n\nTo try your form, visit your site using a URL similar to this one::\n\n\texample.com/index.php/form/\n\nIf you submit the form you should simply see the form reload. That's\nbecause you haven't set up any validation rules yet.\n\n**Since you haven't told the Form Validation class to validate anything\nyet, it returns FALSE (boolean false) by default. ``The run()`` method\nonly returns TRUE if it has successfully applied your rules without any\nof them failing.**\n\nExplanation\n===========\n\nYou'll notice several things about the above pages:\n\nThe form (myform.php) is a standard web form with a couple exceptions:\n\n#. It uses a form helper to create the form opening. Technically, this\n   isn't necessary. You could create the form using standard HTML.\n   However, the benefit of using the helper is that it generates the\n   action URL for you, based on the URL in your config file. This makes\n   your application more portable in the event your URLs change.\n#. At the top of the form you'll notice the following function call:\n   ::\n\n\t<?php echo validation_errors(); ?>\n\n   This function will return any error messages sent back by the\n   validator. If there are no messages it returns an empty string.\n\nThe controller (Form.php) has one method: ``index()``. This method\ninitializes the validation class and loads the form helper and URL\nhelper used by your view files. It also runs the validation routine.\nBased on whether the validation was successful it either presents the\nform or the success page.\n\n.. _setting-validation-rules:\n\nSetting Validation Rules\n========================\n\nCodeIgniter lets you set as many validation rules as you need for a\ngiven field, cascading them in order, and it even lets you prep and\npre-process the field data at the same time. To set validation rules you\nwill use the ``set_rules()`` method::\n\n\t$this->form_validation->set_rules();\n\nThe above method takes **three** parameters as input:\n\n#. The field name - the exact name you've given the form field.\n#. A \"human\" name for this field, which will be inserted into the error\n   message. For example, if your field is named \"user\" you might give it\n   a human name of \"Username\".\n#. The validation rules for this form field.\n#. (optional) Set custom error messages on any rules given for current field. If not provided will use the default one.\n\n.. note:: If you would like the field name to be stored in a language\n\tfile, please see :ref:`translating-field-names`.\n\nHere is an example. In your controller (Form.php), add this code just\nbelow the validation initialization method::\n\n\t$this->form_validation->set_rules('username', 'Username', 'required');\n\t$this->form_validation->set_rules('password', 'Password', 'required');\n\t$this->form_validation->set_rules('passconf', 'Password Confirmation', 'required');\n\t$this->form_validation->set_rules('email', 'Email', 'required');\n\nYour controller should now look like this::\n\n\t<?php\n\n\tclass Form extends CI_Controller {\n\n\t\tpublic function index()\n\t\t{\n\t\t\t$this->load->helper(array('form', 'url'));\n\n\t\t\t$this->load->library('form_validation');\n\n\t\t\t$this->form_validation->set_rules('username', 'Username', 'required');\n\t\t\t$this->form_validation->set_rules('password', 'Password', 'required',\n\t\t\t\tarray('required' => 'You must provide a %s.')\n\t\t\t);\n\t\t\t$this->form_validation->set_rules('passconf', 'Password Confirmation', 'required');\n\t\t\t$this->form_validation->set_rules('email', 'Email', 'required');\n\n\t\t\tif ($this->form_validation->run() == FALSE)\n\t\t\t{\n\t\t\t\t$this->load->view('myform');\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$this->load->view('formsuccess');\n\t\t\t}\n\t\t}\n\t}\n\nNow submit the form with the fields blank and you should see the error\nmessages. If you submit the form with all the fields populated you'll\nsee your success page.\n\n.. note:: The form fields are not yet being re-populated with the data\n\twhen there is an error. We'll get to that shortly.\n\nSetting Rules Using an Array\n============================\n\nBefore moving on it should be noted that the rule setting method can\nbe passed an array if you prefer to set all your rules in one action. If\nyou use this approach, you must name your array keys as indicated::\n\n\t$config = array(\n\t\tarray(\n\t\t\t'field' => 'username',\n\t\t\t'label' => 'Username',\n\t\t\t'rules' => 'required'\n\t\t),\n\t\tarray(\n\t\t\t'field' => 'password',\n\t\t\t'label' => 'Password',\n\t\t\t'rules' => 'required',\n\t\t\t'errors' => array(\n\t\t\t\t'required' => 'You must provide a %s.',\n\t\t\t),\n\t\t),\n\t\tarray(\n\t\t\t'field' => 'passconf',\n\t\t\t'label' => 'Password Confirmation',\n\t\t\t'rules' => 'required'\n\t\t),\n\t\tarray(\n\t\t\t'field' => 'email',\n\t\t\t'label' => 'Email',\n\t\t\t'rules' => 'required'\n\t\t)\n\t);\n\n\t$this->form_validation->set_rules($config);\n\nCascading Rules\n===============\n\nCodeIgniter lets you pipe multiple rules together. Let's try it. Change\nyour rules in the third parameter of rule setting method, like this::\n\n\t$this->form_validation->set_rules(\n\t\t'username', 'Username',\n\t\t'required|min_length[5]|max_length[12]|is_unique[users.username]',\n\t\tarray(\n\t\t\t'required'\t=> 'You have not provided %s.',\n\t\t\t'is_unique'\t=> 'This %s already exists.'\n\t\t)\n\t);\n\t$this->form_validation->set_rules('password', 'Password', 'required');\n\t$this->form_validation->set_rules('passconf', 'Password Confirmation', 'required|matches[password]');\n\t$this->form_validation->set_rules('email', 'Email', 'required|valid_email|is_unique[users.email]');\n\nThe above code sets the following rules:\n\n#. The username field be no shorter than 5 characters and no longer than\n   12.\n#. The password field must match the password confirmation field.\n#. The email field must contain a valid email address.\n\nGive it a try! Submit your form without the proper data and you'll see\nnew error messages that correspond to your new rules. There are numerous\nrules available which you can read about in the validation reference.\n\n.. note:: You can also pass an array of rules to ``set_rules()``,\n\tinstead of a string. Example::\n\n\t$this->form_validation->set_rules('username', 'Username', array('required', 'min_length[5]'));\n\nPrepping Data\n=============\n\nIn addition to the validation method like the ones we used above, you\ncan also prep your data in various ways. For example, you can set up\nrules like this::\n\n\t$this->form_validation->set_rules('username', 'Username', 'trim|required|min_length[5]|max_length[12]');\n\t$this->form_validation->set_rules('password', 'Password', 'trim|required|min_length[8]');\n\t$this->form_validation->set_rules('passconf', 'Password Confirmation', 'trim|required|matches[password]');\n\t$this->form_validation->set_rules('email', 'Email', 'trim|required|valid_email');\n\nIn the above example, we are \"trimming\" the fields, checking for length\nwhere necessary and making sure that both password fields match.\n\n**Any native PHP function that accepts one parameter can be used as a\nrule, like ``htmlspecialchars()``, ``trim()``, etc.**\n\n.. note:: You will generally want to use the prepping functions\n\t**after** the validation rules so if there is an error, the\n\toriginal data will be shown in the form.\n\nRe-populating the form\n======================\n\nThus far we have only been dealing with errors. It's time to repopulate\nthe form field with the submitted data. CodeIgniter offers several\nhelper functions that permit you to do this. The one you will use most\ncommonly is::\n\n\tset_value('field name')\n\nOpen your myform.php view file and update the **value** in each field\nusing the :php:func:`set_value()` function:\n\n**Don't forget to include each field name in the :php:func:`set_value()`\nfunction calls!**\n\n::\n\n\t<html lang=\"en\">\n\t<head>\n\t<title>My Form</title>\n\t</head>\n\t<body>\n\n\t<?php echo validation_errors(); ?>\n\n\t<?php echo form_open('form'); ?>\n\n\t<h5>Username</h5>\n\t<input type=\"text\" name=\"username\" value=\"<?php echo set_value('username'); ?>\" size=\"50\" />\n\n\t<h5>Password</h5>\n\t<input type=\"text\" name=\"password\" value=\"<?php echo set_value('password'); ?>\" size=\"50\" />\n\n\t<h5>Password Confirm</h5>\n\t<input type=\"text\" name=\"passconf\" value=\"<?php echo set_value('passconf'); ?>\" size=\"50\" />\n\n\t<h5>Email Address</h5>\n\t<input type=\"text\" name=\"email\" value=\"<?php echo set_value('email'); ?>\" size=\"50\" />\n\n\t<div><input type=\"submit\" value=\"Submit\" /></div>\n\n\t</form>\n\n\t</body>\n\t</html>\n\nNow reload your page and submit the form so that it triggers an error.\nYour form fields should now be re-populated\n\n.. note:: The :ref:`class-reference` section below\n\tcontains methods that permit you to re-populate <select> menus,\n\tradio buttons, and checkboxes.\n\n.. important:: If you use an array as the name of a form field, you\n\tmust supply it as an array to the function. Example::\n\n\t<input type=\"text\" name=\"colors[]\" value=\"<?php echo set_value('colors[]'); ?>\" size=\"50\" />\n\nFor more info please see the :ref:`using-arrays-as-field-names` section below.\n\nCallbacks: Your own Validation Methods\n======================================\n\nThe validation system supports callbacks to your own validation\nmethods. This permits you to extend the validation class to meet your\nneeds. For example, if you need to run a database query to see if the\nuser is choosing a unique username, you can create a callback method\nthat does that. Let's create an example of this.\n\nIn your controller, change the \"username\" rule to this::\n\n\t$this->form_validation->set_rules('username', 'Username', 'callback_username_check');\n\nThen add a new method called ``username_check()`` to your controller.\nHere's how your controller should now look::\n\n\t<?php\n\n\tclass Form extends CI_Controller {\n\n\t\tpublic function index()\n\t\t{\n\t\t\t$this->load->helper(array('form', 'url'));\n\n\t\t\t$this->load->library('form_validation');\n\n\t\t\t$this->form_validation->set_rules('username', 'Username', 'callback_username_check');\n\t\t\t$this->form_validation->set_rules('password', 'Password', 'required');\n\t\t\t$this->form_validation->set_rules('passconf', 'Password Confirmation', 'required');\n\t\t\t$this->form_validation->set_rules('email', 'Email', 'required|is_unique[users.email]');\n\n\t\t\tif ($this->form_validation->run() == FALSE)\n\t\t\t{\n\t\t\t\t$this->load->view('myform');\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$this->load->view('formsuccess');\n\t\t\t}\n\t\t}\n\n\t\tpublic function username_check($str)\n\t\t{\n\t\t\tif ($str == 'test')\n\t\t\t{\n\t\t\t\t$this->form_validation->set_message('username_check', 'The {field} field can not be the word \"test\"');\n\t\t\t\treturn FALSE;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\treturn TRUE;\n\t\t\t}\n\t\t}\n\n\t}\n\nReload your form and submit it with the word \"test\" as the username. You\ncan see that the form field data was passed to your callback method\nfor you to process.\n\nTo invoke a callback just put the method name in a rule, with\n\"callback\\_\" as the rule **prefix**. If you need to receive an extra\nparameter in your callback method, just add it normally after the\nmethod name between square brackets, as in: ``callback_foo[bar]``,\nthen it will be passed as the second argument of your callback method.\n\n.. note:: You can also process the form data that is passed to your\n\tcallback and return it. If your callback returns anything other than a\n\tboolean TRUE/FALSE it is assumed that the data is your newly processed\n\tform data.\n\nCallable: Use anything as a rule\n================================\n\nIf callback rules aren't good enough for you (for example, because they are\nlimited to your controller), don't get disappointed, there's one more way\nto create custom rules: anything that ``is_callable()`` would return TRUE for.\n\nConsider the following example::\n\n\t$this->form_validation->set_rules(\n\t\t'username', 'Username',\n\t\tarray(\n\t\t\t'required',\n\t\t\tarray($this->users_model, 'valid_username')\n\t\t)\n\t);\n\nThe above code would use the ``valid_username()`` method from your\n``Users_model`` object.\n\nThis is just an example of course, and callbacks aren't limited to models.\nYou can use any object/method that accepts the field value as its' first\nparameter. You can also use an anonymous function::\n\n\t$this->form_validation->set_rules(\n\t\t'username', 'Username',\n\t\tarray(\n\t\t\t'required',\n\t\t\tfunction($value)\n\t\t\t{\n\t\t\t\t// Check $value\n\t\t\t}\n\t\t)\n\t);\n\nOf course, since a Callable rule by itself is not a string, it isn't\na rule name either. That is a problem when you want to set error messages\nfor them. In order to get around that problem, you can put such rules as\nthe second element of an array, with the first one being the rule name::\n\n\t$this->form_validation->set_rules(\n\t\t'username', 'Username',\n\t\tarray(\n\t\t\t'required',\n\t\t\tarray('username_callable', array($this->users_model, 'valid_username'))\n\t\t)\n\t);\n\nAnonymous function version::\n\n\t$this->form_validation->set_rules(\n\t\t'username', 'Username',\n\t\tarray(\n\t\t\t'required',\n\t\t\tarray(\n\t\t\t\t'username_callable',\n\t\t\t\tfunction($str)\n\t\t\t\t{\n\t\t\t\t\t// Check validity of $str and return TRUE or FALSE\n\t\t\t\t}\n\t\t\t)\n\t\t)\n\t);\n\n.. _setting-error-messages:\n\nSetting Error Messages\n======================\n\nAll of the native error messages are located in the following language\nfile: **system/language/english/form_validation_lang.php**\n\nTo set your own global custom message for a rule, you can either \nextend/override the language file by creating your own in\n**application/language/english/form_validation_lang.php** (read more\nabout this in the :doc:`Language Class <language>` documentation),\nor use the following method::\n\n\t$this->form_validation->set_message('rule', 'Error Message');\n\nIf you need to set a custom error message for a particular field on \nsome particular rule, use the set_rules() method::\n\n\t$this->form_validation->set_rules('field_name', 'Field Label', 'rule1|rule2|rule3',\n\t\tarray('rule2' => 'Error Message on rule2 for this field_name')\n\t);\n\nWhere rule corresponds to the name of a particular rule, and Error\nMessage is the text you would like displayed.\n\nIf you'd like to include a field's \"human\" name, or the optional\nparameter some rules allow for (such as max_length), you can add the\n**{field}** and **{param}** tags to your message, respectively::\n\n\t$this->form_validation->set_message('min_length', '{field} must have at least {param} characters.');\n\nOn a field with the human name Username and a rule of min_length[5], an\nerror would display: \"Username must have at least 5 characters.\"\n\n.. note:: The old `sprintf()` method of using **%s** in your error messages\n\twill still work, however it will override the tags above. You should\n\tuse one or the other.\n\nIn the callback rule example above, the error message was set by passing\nthe name of the method (without the \"callback\\_\" prefix)::\n\n\t$this->form_validation->set_message('username_check')\n\n.. _translating-field-names:\n\nTranslating Field Names\n=======================\n\nIf you would like to store the \"human\" name you passed to the\n``set_rules()`` method in a language file, and therefore make the name\nable to be translated, here's how:\n\nFirst, prefix your \"human\" name with **lang:**, as in this example::\n\n\t $this->form_validation->set_rules('first_name', 'lang:first_name', 'required');\n\nThen, store the name in one of your language file arrays (without the\nprefix)::\n\n\t$lang['first_name'] = 'First Name';\n\n.. note:: If you store your array item in a language file that is not\n\tloaded automatically by CI, you'll need to remember to load it in your\n\tcontroller using::\n\n\t$this->lang->load('file_name');\n\nSee the :doc:`Language Class <language>` page for more info regarding\nlanguage files.\n\n.. _changing-delimiters:\n\nChanging the Error Delimiters\n=============================\n\nBy default, the Form Validation class adds a paragraph tag (<p>) around\neach error message shown. You can either change these delimiters\nglobally, individually, or change the defaults in a config file.\n\n#. **Changing delimiters Globally**\n   To globally change the error delimiters, in your controller method,\n   just after loading the Form Validation class, add this::\n\n      $this->form_validation->set_error_delimiters('<div class=\"error\">', '</div>');\n\n   In this example, we've switched to using div tags.\n\n#. **Changing delimiters Individually**\n   Each of the two error generating functions shown in this tutorial can\n   be supplied their own delimiters as follows::\n\n      <?php echo form_error('field name', '<div class=\"error\">', '</div>'); ?>\n\n   Or::\n\n      <?php echo validation_errors('<div class=\"error\">', '</div>'); ?>\n\n#. **Set delimiters in a config file**\n   You can add your error delimiters in application/config/form_validation.php as follows::\n\n      $config['error_prefix'] = '<div class=\"error_prefix\">';\n      $config['error_suffix'] = '</div>';\n\nShowing Errors Individually\n===========================\n\nIf you prefer to show an error message next to each form field, rather\nthan as a list, you can use the :php:func:`form_error()` function.\n\nTry it! Change your form so that it looks like this::\n\n\t<h5>Username</h5>\n\t<?php echo form_error('username'); ?>\n\t<input type=\"text\" name=\"username\" value=\"<?php echo set_value('username'); ?>\" size=\"50\" />\n\n\t<h5>Password</h5>\n\t<?php echo form_error('password'); ?>\n\t<input type=\"text\" name=\"password\" value=\"<?php echo set_value('password'); ?>\" size=\"50\" />\n\n\t<h5>Password Confirm</h5>\n\t<?php echo form_error('passconf'); ?>\n\t<input type=\"text\" name=\"passconf\" value=\"<?php echo set_value('passconf'); ?>\" size=\"50\" />\n\n\t<h5>Email Address</h5>\n\t<?php echo form_error('email'); ?>\n\t<input type=\"text\" name=\"email\" value=\"<?php echo set_value('email'); ?>\" size=\"50\" />\n\nIf there are no errors, nothing will be shown. If there is an error, the\nmessage will appear.\n\n.. important:: If you use an array as the name of a form field, you\n\tmust supply it as an array to the function. Example::\n\n\t<?php echo form_error('options[size]'); ?>\n\t<input type=\"text\" name=\"options[size]\" value=\"<?php echo set_value(\"options[size]\"); ?>\" size=\"50\" />\n\nFor more info please see the :ref:`using-arrays-as-field-names` section below.\n\nValidating an Array (other than $_POST)\n=======================================\n\nSometimes you may want to validate an array that does not originate from ``$_POST`` data.\n\nIn this case, you can specify the array to be validated::\n\n\t$data = array(\n\t\t'username' => 'johndoe',\n\t\t'password' => 'mypassword',\n\t\t'passconf' => 'mypassword'\n\t);\n\n\t$this->form_validation->set_data($data);\n\nCreating validation rules, running the validation, and retrieving error\nmessages works the same whether you are validating ``$_POST`` data or\nanother array of your choice.\n\n.. important:: You have to call the ``set_data()`` method *before* defining\n\tany validation rules.\n\n.. important:: If you want to validate more than one array during a single\n\texecution, then you should call the ``reset_validation()`` method\n\tbefore setting up rules and validating the new array.\n\nFor more info please see the :ref:`class-reference` section below.\n\n.. _saving-groups:\n\n************************************************\nSaving Sets of Validation Rules to a Config File\n************************************************\n\nA nice feature of the Form Validation class is that it permits you to\nstore all your validation rules for your entire application in a config\nfile. You can organize these rules into \"groups\". These groups can\neither be loaded automatically when a matching controller/method is\ncalled, or you can manually call each set as needed.\n\nHow to save your rules\n======================\n\nTo store your validation rules, simply create a file named\nform_validation.php in your application/config/ folder. In that file\nyou will place an array named $config with your rules. As shown earlier,\nthe validation array will have this prototype::\n\n\t$config = array(\n\t\tarray(\n\t\t\t'field' => 'username',\n\t\t\t'label' => 'Username',\n\t\t\t'rules' => 'required'\n\t\t),\n\t\tarray(\n\t\t\t'field' => 'password',\n\t\t\t'label' => 'Password',\n\t\t\t'rules' => 'required'\n\t\t),\n\t\tarray(\n\t\t\t'field' => 'passconf',\n\t\t\t'label' => 'Password Confirmation',\n\t\t\t'rules' => 'required'\n\t\t),\n\t\tarray(\n\t\t\t'field' => 'email',\n\t\t\t'label' => 'Email',\n\t\t\t'rules' => 'required'\n\t\t)\n\t);\n\nYour validation rule file will be loaded automatically and used when you\ncall the ``run()`` method.\n\nPlease note that you MUST name your ``$config`` array.\n\nCreating Sets of Rules\n======================\n\nIn order to organize your rules into \"sets\" requires that you place them\ninto \"sub arrays\". Consider the following example, showing two sets of\nrules. We've arbitrarily called these two rules \"signup\" and \"email\".\nYou can name your rules anything you want::\n\n\t$config = array(\n\t\t'signup' => array(\n\t\t\tarray(\n\t\t\t\t'field' => 'username',\n\t\t\t\t'label' => 'Username',\n\t\t\t\t'rules' => 'required'\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'field' => 'password',\n\t\t\t\t'label' => 'Password',\n\t\t\t\t'rules' => 'required'\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'field' => 'passconf',\n\t\t\t\t'label' => 'Password Confirmation',\n\t\t\t\t'rules' => 'required'\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'field' => 'email',\n\t\t\t\t'label' => 'Email',\n\t\t\t\t'rules' => 'required'\n\t\t\t)\n\t\t),\n\t\t'email' => array(\n\t\t\tarray(\n\t\t\t\t'field' => 'emailaddress',\n\t\t\t\t'label' => 'EmailAddress',\n\t\t\t\t'rules' => 'required|valid_email'\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'field' => 'name',\n\t\t\t\t'label' => 'Name',\n\t\t\t\t'rules' => 'required|alpha'\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'field' => 'title',\n\t\t\t\t'label' => 'Title',\n\t\t\t\t'rules' => 'required'\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'field' => 'message',\n\t\t\t\t'label' => 'MessageBody',\n\t\t\t\t'rules' => 'required'\n\t\t\t)\n\t\t)\n\t);\n\nCalling a Specific Rule Group\n=============================\n\nIn order to call a specific group, you will pass its name to the ``run()``\nmethod. For example, to call the signup rule you will do this::\n\n\tif ($this->form_validation->run('signup') == FALSE)\n\t{\n\t\t$this->load->view('myform');\n\t}\n\telse\n\t{\n\t\t$this->load->view('formsuccess');\n\t}\n\nAssociating a Controller Method with a Rule Group\n=================================================\n\nAn alternate (and more automatic) method of calling a rule group is to\nname it according to the controller class/method you intend to use it\nwith. For example, let's say you have a controller named Member and a\nmethod named signup. Here's what your class might look like::\n\n\t<?php\n\n\tclass Member extends CI_Controller {\n\n\t\tpublic function signup()\n\t\t{\n\t\t\t$this->load->library('form_validation');\n\n\t\t\tif ($this->form_validation->run() == FALSE)\n\t\t\t{\n\t\t\t\t$this->load->view('myform');\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$this->load->view('formsuccess');\n\t\t\t}\n\t\t}\n\t}\n\nIn your validation config file, you will name your rule group\nmember/signup::\n\n\t$config = array(\n\t\t'member/signup' => array(\n\t\t\tarray(\n\t\t\t\t'field' => 'username',\n\t\t\t\t'label' => 'Username',\n\t\t\t\t'rules' => 'required'\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'field' => 'password',\n\t\t\t\t'label' => 'Password',\n\t\t\t\t'rules' => 'required'\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'field' => 'passconf',\n\t\t\t\t'label' => 'PasswordConfirmation',\n\t\t\t\t'rules' => 'required'\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'field' => 'email',\n\t\t\t\t'label' => 'Email',\n\t\t\t\t'rules' => 'required'\n\t\t\t)\n\t\t)\n\t);\n\nWhen a rule group is named identically to a controller class/method it\nwill be used automatically when the ``run()`` method is invoked from that\nclass/method.\n\nAccessing validated/processed data\n==================================\n\nBy default, validation will be performed directly on the ``$_POST`` array,\nand any possible modifications (like trimming whitespace, for example)\nwould be written back onto it.  \nHowever, if you want to keep the original input data intact, or have used\n``set_data()`` to pass a custom set of inputs, you would likely want to\nfetch the now-modified data. In order to do that, you can pass a variable\nas the second parameter to ``run()``::\n\n\t$input  = array('name' => '   White Space  ');\n\t$output = NULL;\n\t\n\t$this->form_validation->set_rules('name', 'Name', 'required|trim');\n\t$this->form_validation->run(NULL, $output);\n\t// $output will now contain: array('name' => 'White Space');\n\n.. _using-arrays-as-field-names:\n\n***************************\nUsing Arrays as Field Names\n***************************\n\nThe Form Validation class supports the use of arrays as field names.\nConsider this example::\n\n\t<input type=\"text\" name=\"options[]\" value=\"\" size=\"50\" />\n\nIf you do use an array as a field name, you must use the EXACT array\nname in the :ref:`Helper Functions <helper-functions>` that require the\nfield name, and as your Validation Rule field name.\n\nFor example, to set a rule for the above field you would use::\n\n\t$this->form_validation->set_rules('options[]', 'Options', 'required');\n\nOr, to show an error for the above field you would use::\n\n\t<?php echo form_error('options[]'); ?>\n\nOr to re-populate the field you would use::\n\n\t<input type=\"text\" name=\"options[]\" value=\"<?php echo set_value('options[]'); ?>\" size=\"50\" />\n\nYou can use multidimensional arrays as field names as well. For example::\n\n\t<input type=\"text\" name=\"options[size]\" value=\"\" size=\"50\" />\n\nOr even::\n\n\t<input type=\"text\" name=\"sports[nba][basketball]\" value=\"\" size=\"50\" />\n\nAs with our first example, you must use the exact array name in the\nhelper functions::\n\n\t<?php echo form_error('sports[nba][basketball]'); ?>\n\nIf you are using checkboxes (or other fields) that have multiple\noptions, don't forget to leave an empty bracket after each option, so\nthat all selections will be added to the POST array::\n\n\t<input type=\"checkbox\" name=\"options[]\" value=\"red\" />\n\t<input type=\"checkbox\" name=\"options[]\" value=\"blue\" />\n\t<input type=\"checkbox\" name=\"options[]\" value=\"green\" />\n\nOr if you use a multidimensional array::\n\n\t<input type=\"checkbox\" name=\"options[color][]\" value=\"red\" />\n\t<input type=\"checkbox\" name=\"options[color][]\" value=\"blue\" />\n\t<input type=\"checkbox\" name=\"options[color][]\" value=\"green\" />\n\nWhen you use a helper function you'll include the bracket as well::\n\n\t<?php echo form_error('options[color][]'); ?>\n\n\n**************\nRule Reference\n**************\n\nThe following is a list of all the native rules that are available to\nuse:\n\n========================= ========== ============================================================================================= =======================\nRule                      Parameter  Description                                                                                   Example\n========================= ========== ============================================================================================= =======================\n**required**              No         Returns FALSE if the form element is empty.\n**matches**               Yes        Returns FALSE if the form element does not match the one in the parameter.                    matches[form_item]\n**regex_match**           Yes        Returns FALSE if the form element does not match the regular expression.                      regex_match[/regex/]\n**differs**               Yes        Returns FALSE if the form element does not differ from the one in the parameter.              differs[form_item]\n**is_unique**             Yes        Returns FALSE if the form element is not unique to the table and field name in the            is_unique[table.field]\n                                     parameter. Note: This rule requires :doc:`Query Builder <../database/query_builder>` to be\n                                     enabled in order to work.\n**min_length**            Yes        Returns FALSE if the form element is shorter than the parameter value.                        min_length[3]\n**max_length**            Yes        Returns FALSE if the form element is longer than the parameter value.                         max_length[12]\n**exact_length**          Yes        Returns FALSE if the form element is not exactly the parameter value.                         exact_length[8]\n**greater_than**          Yes        Returns FALSE if the form element is less than or equal to the parameter value or not         greater_than[8]\n                                     numeric.\n**greater_than_equal_to** Yes        Returns FALSE if the form element is less than the parameter value,                           greater_than_equal_to[8]\n                                     or not numeric.\n**less_than**             Yes        Returns FALSE if the form element is greater than or equal to the parameter value or          less_than[8]\n                                     not numeric.\n**less_than_equal_to**    Yes        Returns FALSE if the form element is greater than the parameter value,                        less_than_equal_to[8]\n                                     or not numeric.\n**in_list**               Yes        Returns FALSE if the form element is not within a predetermined list.                         in_list[red,blue,green]\n**alpha**                 No         Returns FALSE if the form element contains anything other than alphabetical characters.\n**alpha_numeric**         No         Returns FALSE if the form element contains anything other than alpha-numeric characters.\n**alpha_numeric_spaces**  No         Returns FALSE if the form element contains anything other than alpha-numeric characters\n                                     or spaces.  Should be used after trim to avoid spaces at the beginning or end.\n**alpha_dash**            No         Returns FALSE if the form element contains anything other than alpha-numeric characters,\n                                     underscores or dashes.\n**numeric**               No         Returns FALSE if the form element contains anything other than numeric characters.\n**integer**               No         Returns FALSE if the form element contains anything other than an integer.\n**decimal**               No         Returns FALSE if the form element contains anything other than a decimal number.\n**is_natural**            No         Returns FALSE if the form element contains anything other than a natural number:\n                                     0, 1, 2, 3, etc.\n**is_natural_no_zero**    No         Returns FALSE if the form element contains anything other than a natural\n                                     number, but not zero: 1, 2, 3, etc.\n**valid_url**             No         Returns FALSE if the form element does not contain a valid URL.\n**valid_email**           No         Returns FALSE if the form element does not contain a valid email address.\n**valid_emails**          No         Returns FALSE if any value provided in a comma separated list is not a valid email.\n**valid_ip**              Yes        Returns FALSE if the supplied IP address is not valid.\n                                     Accepts an optional parameter of 'ipv4' or 'ipv6' to specify an IP format.\n**valid_mac**             No         Returns FALSE if the supplied MAC address is not valid.\n**valid_base64**          No         Returns FALSE if the supplied string contains anything other than valid Base64 characters.\n========================= ========== ============================================================================================= =======================\n\n.. note:: These rules can also be called as discrete methods. For\n\texample::\n\n\t\t$this->form_validation->required($string);\n\n.. note:: You can also use any native PHP functions that permit up\n\tto two parameters, where at least one is required (to pass\n\tthe field data).\n\n******************\nPrepping Reference\n******************\n\nThe following is a list of all the prepping methods that are available\nto use:\n\n==================== ========= ==============================================================================================================\nName                 Parameter Description\n==================== ========= ==============================================================================================================\n**prep_url**         No        Adds \"\\http://\" to URLs if missing.\n**strip_image_tags** No        Strips the HTML from image tags leaving the raw URL.\n**encode_php_tags**  No        Converts PHP tags to entities.\n==================== ========= ==============================================================================================================\n\n.. note:: You can also use any native PHP functions that permits one\n\tparameter, like ``trim()``, ``htmlspecialchars()``, ``urldecode()``,\n\tetc.\n\n.. _class-reference:\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Form_validation\n\n\t.. php:method:: set_rules($field[, $label = null[, $rules = null[, $errors = array()]]])\n\n\t\t:param\tstring\t$field: Field name\n\t\t:param\tstring\t$label: Field label\n\t\t:param\tmixed\t$rules: Validation rules, as a string list separated by a pipe \"|\", or as an array or rules\n\t\t:param\tarray\t$errors: A list of custom error messages\n\t\t:returns:\tCI_Form_validation instance (method chaining)\n\t\t:throws:\tBadMethodCallException\tIf $field is not an array and $rules was not used\n\t\t:rtype:\tCI_Form_validation\n\n\t\tPermits you to set validation rules, as described in the tutorial\n\t\tsections above:\n\n\t\t-  :ref:`setting-validation-rules`\n\t\t-  :ref:`saving-groups`\n\n\t.. php:method:: run([$config = NULL[, $data = NULL]])\n\n\t\t:param\tstring\t$group: The name of the validation group to run\n\t\t:param\tmixed\t$data: Optional variable to assign validated data to\n\t\t:returns:\tTRUE on success, FALSE if validation failed\n\t\t:rtype:\tbool\n\n\t\tRuns the validation routines. Returns boolean TRUE on success and FALSE\n\t\ton failure. You can optionally pass the name of the validation group via\n\t\tthe method, as described in: :ref:`saving-groups`\n\n\t.. php:method:: set_message($lang[, $val = ''])\n\n\t\t:param\tstring\t$lang: The rule the message is for\n\t\t:param\tstring\t$val: The message\n\t\t:returns:\tCI_Form_validation instance (method chaining)\n\t\t:rtype:\tCI_Form_validation\n\n\t\tPermits you to set custom error messages. See :ref:`setting-error-messages`\n\n\t.. php:method:: set_error_delimiters([$prefix = '<p>'[, $suffix = '</p>']])\n\n\t\t:param\tstring\t$prefix: Error message prefix\n\t\t:param\tstring\t$suffix: Error message suffix\n\t\t:returns:\tCI_Form_validation instance (method chaining)\n\t\t:rtype:\tCI_Form_validation\n\n\t\tSets the default prefix and suffix for error messages.\n\n\t.. php:method:: set_data($data)\n\n\t\t:param\tarray\t$data: Array of data validate\n\t\t:returns:\tCI_Form_validation instance (method chaining)\n\t\t:rtype:\tCI_Form_validation\n\n\t\tPermits you to set an array for validation, instead of using the default\n\t\t``$_POST`` array.\n\n\t.. php:method:: reset_validation()\n\n\t\t:returns:\tCI_Form_validation instance (method chaining)\n\t\t:rtype:\tCI_Form_validation\n\n\t\tPermits you to reset the validation when you validate more than one array.\n\t\tThis method should be called before validating each new array.\n\n\t.. php:method:: error_array()\n\n\t\t:returns:\tArray of error messages\n\t\t:rtype:\tarray\n\n\t\tReturns the error messages as an array.\n\n\t.. php:method:: error_string([$prefix = ''[, $suffix = '']])\n\n\t\t:param\tstring\t$prefix: Error message prefix\n\t\t:param\tstring\t$suffix: Error message suffix\n\t\t:returns:\tError messages as a string\n\t\t:rtype:\tstring\n\n\t\tReturns all error messages (as returned from error_array()) formatted as a\n\t\tstring and separated by a newline character.\n\n\t.. php:method:: error($field[, $prefix = ''[, $suffix = '']])\n\n\t\t:param\tstring $field: Field name\n\t\t:param\tstring $prefix: Optional prefix\n\t\t:param\tstring $suffix: Optional suffix\n\t\t:returns:\tError message string\n\t\t:rtype:\tstring\n\n\t\tReturns the error message for a specific field, optionally adding a\n\t\tprefix and/or suffix to it (usually HTML tags).\n\n\t.. php:method:: has_rule($field)\n\n\t\t:param\tstring\t$field: Field name\n\t\t:returns:\tTRUE if the field has rules set, FALSE if not\n\t\t:rtype:\tbool\n\n\t\tChecks to see if there is a rule set for the specified field.\n\n.. _helper-functions:\n\n****************\nHelper Reference\n****************\n\nPlease refer to the :doc:`Form Helper <../helpers/form_helper>` manual for\nthe following functions:\n\n-  :php:func:`form_error()`\n-  :php:func:`validation_errors()`\n-  :php:func:`set_value()`\n-  :php:func:`set_select()`\n-  :php:func:`set_checkbox()`\n-  :php:func:`set_radio()`\n\nNote that these are procedural functions, so they **do not** require you\nto prepend them with ``$this->form_validation``.\n"
  },
  {
    "path": "user_guide_src/source/libraries/ftp.rst",
    "content": "#########\nFTP Class\n#########\n\nCodeIgniter's FTP Class permits files to be transferred to a remote\nserver. Remote files can also be moved, renamed, and deleted. The FTP\nclass also includes a \"mirroring\" function that permits an entire local\ndirectory to be recreated remotely via FTP.\n\n.. note:: SFTP and SSL FTP protocols are not supported, only standard\n\tFTP.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n**************************\nWorking with the FTP Class\n**************************\n\nInitializing the Class\n======================\n\nLike most other classes in CodeIgniter, the FTP class is initialized in\nyour controller using the $this->load->library function::\n\n\t$this->load->library('ftp');\n\nOnce loaded, the FTP object will be available using: $this->ftp\n\nUsage Examples\n==============\n\nIn this example a connection is opened to the FTP server, and a local\nfile is read and uploaded in ASCII mode. The file permissions are set to\n755.\n::\n\n\t$this->load->library('ftp');\n\n\t$config['hostname'] = 'ftp.example.com';\n\t$config['username'] = 'your-username';\n\t$config['password'] = 'your-password';\n\t$config['debug']\t= TRUE;\n\n\t$this->ftp->connect($config);\n\n\t$this->ftp->upload('/local/path/to/myfile.html', '/public_html/myfile.html', 'ascii', 0775);\n\n\t$this->ftp->close();\n\nIn this example a list of files is retrieved from the server.\n::\n\n\t$this->load->library('ftp');\n\n\t$config['hostname'] = 'ftp.example.com';\n\t$config['username'] = 'your-username';\n\t$config['password'] = 'your-password';\n\t$config['debug']\t= TRUE;\n\n\t$this->ftp->connect($config);\n\n\t$list = $this->ftp->list_files('/public_html/');\n\n\tprint_r($list);\n\n\t$this->ftp->close();\n\nIn this example a local directory is mirrored on the server.\n::\n\n\t$this->load->library('ftp');\n\n\t$config['hostname'] = 'ftp.example.com';\n\t$config['username'] = 'your-username';\n\t$config['password'] = 'your-password';\n\t$config['debug']\t= TRUE;\n\n\t$this->ftp->connect($config);\n\n\t$this->ftp->mirror('/path/to/myfolder/', '/public_html/myfolder/');\n\n\t$this->ftp->close();\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_FTP\n\n\t.. php:method:: connect([$config = array()])\n\n\t\t:param\tarray\t$config: Connection values\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tConnects and logs into to the FTP server. Connection preferences are set\n\t\tby passing an array to the function, or you can store them in a config\n\t\tfile.\n\n\t\tHere is an example showing how you set preferences manually::\n\n\t\t\t$this->load->library('ftp');\n\n\t\t\t$config['hostname'] = 'ftp.example.com';\n\t\t\t$config['username'] = 'your-username';\n\t\t\t$config['password'] = 'your-password';\n\t\t\t$config['port']     = 21;\n\t\t\t$config['passive']  = FALSE;\n\t\t\t$config['debug']    = TRUE;\n\n\t\t\t$this->ftp->connect($config);\n\n\t\t**Setting FTP Preferences in a Config File**\n\n\t\tIf you prefer you can store your FTP preferences in a config file.\n\t\tSimply create a new file called the ftp.php, add the $config array in\n\t\tthat file. Then save the file at *application/config/ftp.php* and it\n\t\twill be used automatically.\n\n\t\t**Available connection options**\n\n\t\t============== =============== =============================================================================\n\t\tOption name    Default value   Description\n\t\t============== =============== =============================================================================\n\t\t**hostname**   n/a             FTP hostname (usually something like: ftp.example.com)\n\t\t**username**   n/a             FTP username\n\t\t**password**   n/a             FTP password\n\t\t**port**       21              FTP server port number\n\t\t**debug**      FALSE           TRUE/FALSE (boolean): Whether to enable debugging to display error messages\n\t\t**passive**    TRUE            TRUE/FALSE (boolean): Whether to use passive mode\n\t\t============== =============== =============================================================================\n\n\t.. php:method:: upload($locpath, $rempath[, $mode = 'auto'[, $permissions = NULL]])\n\n\t\t:param\tstring\t$locpath: Local file path\n\t\t:param\tstring\t$rempath: Remote file path\n\t\t:param\tstring\t$mode: FTP mode, defaults to 'auto' (options are: 'auto', 'binary', 'ascii')\n\t\t:param\tint\t$permissions: File permissions (octal)\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tUploads a file to your server. You must supply the local path and the\n\t\tremote path, and you can optionally set the mode and permissions.\n\t\tExample::\n\n\t\t\t$this->ftp->upload('/local/path/to/myfile.html', '/public_html/myfile.html', 'ascii', 0775);\n\n\t\tIf 'auto' mode is used it will base the mode on the file extension of the source file.\n\n\t\tIf set, permissions have to be passed as an octal value.\n\n\t.. php:method:: download($rempath, $locpath[, $mode = 'auto'])\n\n\t\t:param\tstring\t$rempath: Remote file path\n\t\t:param\tstring\t$locpath: Local file path\n\t\t:param\tstring\t$mode: FTP mode, defaults to 'auto' (options are: 'auto', 'binary', 'ascii')\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tDownloads a file from your server. You must supply the remote path and\n\t\tthe local path, and you can optionally set the mode. Example::\n\n\t\t\t$this->ftp->download('/public_html/myfile.html', '/local/path/to/myfile.html', 'ascii');\n\n\t\tIf 'auto' mode is used it will base the mode on the file extension of the source file.\n\n\t\tReturns FALSE if the download does not execute successfully\n\t\t(including if PHP does not have permission to write the local file).\n\n\t.. php:method:: rename($old_file, $new_file[, $move = FALSE])\n\n\t\t:param\tstring\t$old_file: Old file name\n\t\t:param\tstring\t$new_file: New file name\n\t\t:param\tbool\t$move: Whether a move is being performed\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tPermits you to rename a file. Supply the source file name/path and the new file name/path.\n\t\t::\n\n\t\t\t// Renames green.html to blue.html\n\t\t\t$this->ftp->rename('/public_html/foo/green.html', '/public_html/foo/blue.html');\n\n\t.. php:method:: move($old_file, $new_file)\n\n\t\t:param\tstring\t$old_file: Old file name\n\t\t:param\tstring\t$new_file: New file name\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tLets you move a file. Supply the source and destination paths::\n\n\t\t\t// Moves blog.html from \"joe\" to \"fred\"\n\t\t\t$this->ftp->move('/public_html/joe/blog.html', '/public_html/fred/blog.html');\n\n\t\t.. note:: If the destination file name is different the file will be renamed.\n\n\t.. php:method:: delete_file($filepath)\n\n\t\t:param\tstring\t$filepath: Path to file to delete\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tLets you delete a file. Supply the source path with the file name.\n\t\t::\n\n\t\t\t $this->ftp->delete_file('/public_html/joe/blog.html');\n\n\t.. php:method:: delete_dir($filepath)\n\n\t\t:param\tstring\t$filepath: Path to directory to delete\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tLets you delete a directory and everything it contains. Supply the\n\t\tsource path to the directory with a trailing slash.\n\n\t\t.. important:: Be VERY careful with this method!\n\t\t\tIt will recursively delete **everything** within the supplied path,\n\t\t\tincluding sub-folders and all files. Make absolutely sure your path\n\t\t\tis correct. Try using ``list_files()`` first to verify that your path is correct.\n\n\t\t::\n\n\t\t\t $this->ftp->delete_dir('/public_html/path/to/folder/');\n\n\t.. php:method:: list_files([$path = '.'])\n\n\t\t:param\tstring\t$path: Directory path\n\t\t:returns:\tAn array list of files or FALSE on failure\n\t\t:rtype:\tarray\n\n\t\tPermits you to retrieve a list of files on your server returned as an\n\t\tarray. You must supply the path to the desired directory.\n\t\t::\n\n\t\t\t$list = $this->ftp->list_files('/public_html/');\n\t\t\tprint_r($list);\n\n\t.. php:method:: mirror($locpath, $rempath)\n\n\t\t:param\tstring\t$locpath: Local path\n\t\t:param\tstring\t$rempath: Remote path\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tRecursively reads a local folder and everything it contains (including\n\t\tsub-folders) and creates a mirror via FTP based on it. Whatever the\n\t\tdirectory structure of the original file path will be recreated on the\n\t\tserver. You must supply a source path and a destination path::\n\n\t\t\t $this->ftp->mirror('/path/to/myfolder/', '/public_html/myfolder/');\n\n\t.. php:method:: mkdir($path[, $permissions = NULL])\n\n\t\t:param\tstring\t$path: Path to directory to create\n\t\t:param\tint\t$permissions: Permissions (octal)\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tLets you create a directory on your server. Supply the path ending in\n\t\tthe folder name you wish to create, with a trailing slash.\n\n\t\tPermissions can be set by passing an octal value in the second parameter.\n\t\t::\n\n\t\t\t// Creates a folder named \"bar\"\n\t\t\t$this->ftp->mkdir('/public_html/foo/bar/', 0755);\n\n\t.. php:method:: chmod($path, $perm)\n\n\t\t:param\tstring\t$path: Path to alter permissions for\n\t\t:param\tint\t$perm: Permissions (octal)\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tPermits you to set file permissions. Supply the path to the file or\n\t\tdirectory you wish to alter permissions on::\n\n\t\t\t// Chmod \"bar\" to 755\n\t\t\t$this->ftp->chmod('/public_html/foo/bar/', 0755);\n\n\t.. php:method:: changedir($path[, $suppress_debug = FALSE])\n\n\t\t:param\tstring\t$path: Directory path\n\t\t:param\tbool\t$suppress_debug: Whether to turn off debug messages for this command\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tChanges the current working directory to the specified path.\n\n\t\tThe ``$suppress_debug`` parameter is useful in case you want to use this method\n\t\tas an ``is_dir()`` alternative for FTP.\n\n\t.. php:method:: close()\n\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tCloses the connection to your server. It's recommended that you use this\n\t\twhen you are finished uploading."
  },
  {
    "path": "user_guide_src/source/libraries/image_lib.rst",
    "content": "########################\nImage Manipulation Class\n########################\n\nCodeIgniter's Image Manipulation class lets you perform the following\nactions:\n\n-  Image Resizing\n-  Thumbnail Creation\n-  Image Cropping\n-  Image Rotating\n-  Image Watermarking\n\nAll three major image libraries are supported: GD/GD2, NetPBM, and\nImageMagick\n\n.. note:: Watermarking is only available using the GD/GD2 library. In\n\taddition, even though other libraries are supported, GD is required in\n\torder for the script to calculate the image properties. The image\n\tprocessing, however, will be performed with the library you specify.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n**********************\nInitializing the Class\n**********************\n\nLike most other classes in CodeIgniter, the image class is initialized\nin your controller using the $this->load->library function::\n\n\t$this->load->library('image_lib');\n\nOnce the library is loaded it will be ready for use. The image library\nobject you will use to call all functions is: ``$this->image_lib``\n\nProcessing an Image\n===================\n\nRegardless of the type of processing you would like to perform\n(resizing, cropping, rotation, or watermarking), the general process is\nidentical. You will set some preferences corresponding to the action you\nintend to perform, then call one of four available processing functions.\nFor example, to create an image thumbnail you'll do this::\n\n\t$config['image_library'] = 'gd2';\n\t$config['source_image']\t= '/path/to/image/mypic.jpg';\n\t$config['create_thumb'] = TRUE;\n\t$config['maintain_ratio'] = TRUE;\n\t$config['width']\t = 75;\n\t$config['height']\t= 50;\n\n\t$this->load->library('image_lib', $config); \n\n\t$this->image_lib->resize();\n\nThe above code tells the image_resize function to look for an image\ncalled *mypic.jpg* located in the source_image folder, then create a\nthumbnail that is 75 X 50 pixels using the GD2 image_library. Since the\nmaintain_ratio option is enabled, the thumb will be as close to the\ntarget width and height as possible while preserving the original aspect\nratio. The thumbnail will be called *mypic_thumb.jpg* and located at\nthe same level as *source_image*.\n\n.. note:: In order for the image class to be allowed to do any\n\tprocessing, the folder containing the image files must have write\n\tpermissions.\n\n.. note:: Image processing can require a considerable amount of server\n\tmemory for some operations. If you are experiencing out of memory errors\n\twhile processing images you may need to limit their maximum size, and/or\n\tadjust PHP memory limits.\n\nProcessing Methods\n==================\n\nThere are four available processing methods:\n\n-  $this->image_lib->resize()\n-  $this->image_lib->crop()\n-  $this->image_lib->rotate()\n-  $this->image_lib->watermark()\n\nThese methods return boolean TRUE upon success and FALSE for failure.\nIf they fail you can retrieve the error message using this function::\n\n\techo $this->image_lib->display_errors();\n\nA good practice is to use the processing function conditionally, showing an\nerror upon failure, like this::\n\n\tif ( ! $this->image_lib->resize())\n\t{\n\t\techo $this->image_lib->display_errors();\n\t}\n\n.. note:: You can optionally specify the HTML formatting to be applied to\n\tthe errors, by submitting the opening/closing tags in the function,\n\tlike this::\n\n\t$this->image_lib->display_errors('<p>', '</p>');\n\n.. _processing-preferences:\n\nPreferences\n===========\n\nThe preferences described below allow you to tailor the image processing\nto suit your needs.\n\nNote that not all preferences are available for every function. For\nexample, the x/y axis preferences are only available for image cropping.\nLikewise, the width and height preferences have no effect on cropping.\nThe \"availability\" column indicates which functions support a given\npreference.\n\nAvailability Legend:\n\n-  R - Image Resizing\n-  C - Image Cropping\n-  X - Image Rotation\n-  W - Image Watermarking\n\n======================= ======================= =============================== =========================================================================== =============\nPreference              Default Value           Options                         Description                                                                 Availability\n======================= ======================= =============================== =========================================================================== =============\n**image_library**       GD2                     GD, GD2, ImageMagick, NetPBM    Sets the image library to be used.                                          R, C, X, W\n**library_path**        None                    None                            Sets the server path to your ImageMagick or NetPBM library. If you use      R, C, X\n                                                                                either of those libraries you must supply the path.                         R, C, S, W\n**source_image**        None                    None                            Sets the source image name/path. The path must be a relative or absolute\n                                                                                server path, not a URL.\n**dynamic_output**      FALSE                   TRUE/FALSE (boolean)            Determines whether the new image file should be written to disk or          R, C, X, W\n                                                                                generated dynamically. Note: If you choose the dynamic setting, only one\n                                                                                image can be shown at a time, and it can't be positioned on the page. It\n                                                                                simply outputs the raw image dynamically to your browser, along with\n                                                                                image headers.\n**file_permissions**    0644                    (integer)                       File system permissions to apply on the resulting image file,               R, C, X, W\n                                                                                writing it to the disk. WARNING: Use octal integer notation!\n**quality**             90%                     1 - 100%                        Sets the quality of the image. The higher the quality the larger the        R, C, X, W\n                                                                                file size.\n**new_image**           None                    None                            Sets the destination image name/path. You'll use this preference when       R, C, X, W\n                                                                                creating an image copy. The path must be a relative or absolute server\n                                                                                path, not a URL.\n**width**               None                    None                            Sets the width you would like the image set to.                             R, C\n**height**              None                    None                            Sets the height you would like the image set to.                            R, C\n**create_thumb**        FALSE                   TRUE/FALSE (boolean)            Tells the image processing function to create a thumb.                      R\n**thumb_marker**        _thumb                  None                            Specifies the thumbnail indicator. It will be inserted just before the      R\n                                                                                file extension, so mypic.jpg would become mypic_thumb.jpg\n**maintain_ratio**      TRUE                    TRUE/FALSE (boolean)            Specifies whether to maintain the original aspect ratio when resizing or    R, C\n                                                                                use hard values.\n**master_dim**          auto                    auto, width, height             Specifies what to use as the master axis when resizing or creating          R\n                                                                                thumbs. For example, let's say you want to resize an image to 100 X 75\n                                                                                pixels. If the source image size does not allow perfect resizing to\n                                                                                those dimensions, this setting determines which axis should be used as\n                                                                                the hard value. \"auto\" sets the axis automatically based on whether the\n                                                                                image is taller than wider, or vice versa.\n**rotation_angle**      None                    90, 180, 270, vrt, hor          Specifies the angle of rotation when rotating images. Note that PHP         X\n                                                                                rotates counter-clockwise, so a 90 degree rotation to the right must be\n                                                                                specified as 270.\n**x_axis**              None                    None                            Sets the X coordinate in pixels for image cropping. For example, a          C\n                                                                                setting of 30 will crop an image 30 pixels from the left.\n**y_axis**              None                    None                            Sets the Y coordinate in pixels for image cropping. For example, a          C\n                                                                                setting of 30 will crop an image 30 pixels from the top.\n======================= ======================= =============================== =========================================================================== =============\n\nSetting preferences in a config file\n====================================\n\nIf you prefer not to set preferences using the above method, you can\ninstead put them into a config file. Simply create a new file called\nimage_lib.php, add the $config array in that file. Then save the file\nin *config/image_lib.php* and it will be used automatically. You will\nNOT need to use the ``$this->image_lib->initialize()`` method if you save\nyour preferences in a config file.\n\n******************\nImage Watermarking\n******************\n\nThe Watermarking feature requires the GD/GD2 library.\n\nTwo Types of Watermarking\n=========================\n\nThere are two types of watermarking that you can use:\n\n-  **Text**: The watermark message will be generated using text, either\n   with a True Type font that you specify, or using the native text\n   output that the GD library supports. If you use the True Type version\n   your GD installation must be compiled with True Type support (most\n   are, but not all).\n-  **Overlay**: The watermark message will be generated by overlaying an\n   image (usually a transparent PNG or GIF) containing your watermark\n   over the source image.\n\n.. _watermarking:\n\nWatermarking an Image\n=====================\n\nJust as with the other methods (resizing, cropping, and rotating) the\ngeneral process for watermarking involves setting the preferences\ncorresponding to the action you intend to perform, then calling the\nwatermark function. Here is an example::\n\n\t$config['source_image']\t= '/path/to/image/mypic.jpg';\n\t$config['wm_text'] = 'Copyright 2006 - John Doe';\n\t$config['wm_type'] = 'text';\n\t$config['wm_font_path'] = './system/fonts/texb.ttf';\n\t$config['wm_font_size']\t= '16';\n\t$config['wm_font_color'] = 'ffffff';\n\t$config['wm_vrt_alignment'] = 'bottom';\n\t$config['wm_hor_alignment'] = 'center';\n\t$config['wm_padding'] = '20';\n\n\t$this->image_lib->initialize($config); \n\n\t$this->image_lib->watermark();\n\nThe above example will use a 16 pixel True Type font to create the text\n\"Copyright 2006 - John Doe\". The watermark will be positioned at the\nbottom/center of the image, 20 pixels from the bottom of the image.\n\n.. note:: In order for the image class to be allowed to do any\n\tprocessing, the image file must have \"write\" file permissions\n\tFor example, 777.\n\nWatermarking Preferences\n========================\n\nThis table shows the preferences that are available for both types of\nwatermarking (text or overlay)\n\n======================= =================== ======================= ==========================================================================\nPreference              Default Value       Options                 Description\n======================= =================== ======================= ==========================================================================\n**wm_type**             text                text, overlay           Sets the type of watermarking that should be used.\n**source_image**        None                None                    Sets the source image name/path. The path must be a relative or absolute\n                                                                    server path, not a URL.\n**dynamic_output**      FALSE               TRUE/FALSE (boolean)    Determines whether the new image file should be written to disk or\n                                                                    generated dynamically. Note: If you choose the dynamic setting, only one\n                                                                    image can be shown at a time, and it can't be positioned on the page. It\n                                                                    simply outputs the raw image dynamically to your browser, along with\n                                                                    image headers.\n**quality**             90%                 1 - 100%                Sets the quality of the image. The higher the quality the larger the\n                                                                    file size.\n**wm_padding**          None                A number                The amount of padding, set in pixels, that will be applied to the\n                                                                    watermark to set it away from the edge of your images.\n**wm_vrt_alignment**    bottom              top, middle, bottom     Sets the vertical alignment for the watermark image.\n**wm_hor_alignment**    center              left, center, right     Sets the horizontal alignment for the watermark image.\n**wm_hor_offset**       None                None                    You may specify a horizontal offset (in pixels) to apply to the\n                                                                    watermark position. The offset normally moves the watermark to the\n                                                                    right, except if you have your alignment set to \"right\" then your offset\n                                                                    value will move the watermark toward the left of the image.\n**wm_vrt_offset**       None                None                    You may specify a vertical offset (in pixels) to apply to the watermark\n                                                                    position. The offset normally moves the watermark down, except if you\n                                                                    have your alignment set to \"bottom\" then your offset value will move the\n                                                                    watermark toward the top of the image.\n======================= =================== ======================= ==========================================================================\n\nText Preferences\n----------------\n\nThis table shows the preferences that are available for the text type of\nwatermarking.\n\n======================= =================== =================== ==========================================================================\nPreference              Default Value       Options             Description\n======================= =================== =================== ==========================================================================\n**wm_text**             None                None                The text you would like shown as the watermark. Typically this will be a\n                                                                copyright notice.\n**wm_font_path**        None                None                The server path to the True Type Font you would like to use. If you do\n                                                                not use this option, the native GD font will be used.\n**wm_font_size**        16                  None                The size of the text. Note: If you are not using the True Type option\n                                                                above, the number is set using a range of 1 - 5. Otherwise, you can use\n                                                                any valid pixel size for the font you're using.\n**wm_font_color**       ffffff              None                The font color, specified in hex. Both the full 6-length (ie, 993300) and\n                                                                the short three character abbreviated version (ie, fff) are supported.\n**wm_shadow_color**     None                None                The color of the drop shadow, specified in hex. If you leave this blank\n                                                                a drop shadow will not be used. Both the full 6-length (ie, 993300) and\n                                                                the short three character abbreviated version (ie, fff) are supported.\n**wm_shadow_distance**  2                   None                The distance (in pixels) from the font that the drop shadow should\n                                                                appear.\n======================= =================== =================== ==========================================================================\n\nOverlay Preferences\n-------------------\n\nThis table shows the preferences that are available for the overlay type\nof watermarking.\n\n======================= =================== =================== ==========================================================================\nPreference              Default Value       Options             Description\n======================= =================== =================== ==========================================================================\n**wm_overlay_path**     None                None                The server path to the image you wish to use as your watermark. Required\n                                                                only if you are using the overlay method.\n**wm_opacity**          50                  1 - 100             Image opacity. You may specify the opacity (i.e. transparency) of your\n                                                                watermark image. This allows the watermark to be faint and not\n                                                                completely obscure the details from the original image behind it. A 50%\n                                                                opacity is typical.\n**wm_x_transp**         4                   A number            If your watermark image is a PNG or GIF image, you may specify a color\n                                                                on the image to be \"transparent\". This setting (along with the next)\n                                                                will allow you to specify that color. This works by specifying the \"X\"\n                                                                and \"Y\" coordinate pixel (measured from the upper left) within the image\n                                                                that corresponds to a pixel representative of the color you want to be\n                                                                transparent.\n**wm_y_transp**         4                   A number            Along with the previous setting, this allows you to specify the\n                                                                coordinate to a pixel representative of the color you want to be\n                                                                transparent.\n======================= =================== =================== ==========================================================================\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Image_lib\n\n\t.. php:method:: initialize([$props = array()])\n\n\t\t:param\tarray\t$props: Image processing preferences\n\t\t:returns:\tTRUE on success, FALSE in case of invalid settings\n\t\t:rtype:\tbool\n\n\t\tInitializes the class for processing an image.\n\n\t.. php:method:: resize()\n\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tThe image resizing method lets you resize the original image, create a\n\t\tcopy (with or without resizing), or create a thumbnail image.\n\n\t\tFor practical purposes there is no difference between creating a copy\n\t\tand creating a thumbnail except a thumb will have the thumbnail marker\n\t\tas part of the name (i.e. mypic_thumb.jpg).\n\n\t\tAll preferences listed in the :ref:`processing-preferences` table are available for this\n\t\tmethod except these three: *rotation_angle*, *x_axis* and *y_axis*.\n\n\t\t**Creating a Thumbnail**\n\n\t\tThe resizing method will create a thumbnail file (and preserve the\n\t\toriginal) if you set this preference to TRUE::\n\n\t\t\t$config['create_thumb'] = TRUE;\n\n\t\tThis single preference determines whether a thumbnail is created or not.\n\n\t\t**Creating a Copy**\n\n\t\tThe resizing method will create a copy of the image file (and preserve\n\t\tthe original) if you set a path and/or a new filename using this\n\t\tpreference::\n\n\t\t\t$config['new_image'] = '/path/to/new_image.jpg';\n\n\t\tNotes regarding this preference:\n\n\t\t-  If only the new image name is specified it will be placed in the same\n\t\t   folder as the original\n\t\t-  If only the path is specified, the new image will be placed in the\n\t\t   destination with the same name as the original.\n\t\t-  If both the path and image name are specified it will placed in its\n\t\t   own destination and given the new name.\n\n\t\t**Resizing the Original Image**\n\n\t\tIf neither of the two preferences listed above (create_thumb, and\n\t\tnew_image) are used, the resizing method will instead target the\n\t\toriginal image for processing.\n\n\t.. php:method:: crop()\n\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tThe cropping method works nearly identically to the resizing function\n\t\texcept it requires that you set preferences for the X and Y axis (in\n\t\tpixels) specifying where to crop, like this::\n\n\t\t\t$config['x_axis'] = 100;\n\t\t\t$config['y_axis'] = 40;\n\n\t\tAll preferences listed in the :ref:`processing-preferences` table are available for this\n\t\tmethod except these: *rotation_angle*, *create_thumb* and *new_image*.\n\n\t\tHere's an example showing how you might crop an image::\n\n\t\t\t$config['image_library'] = 'imagemagick';\n\t\t\t$config['library_path'] = '/usr/X11R6/bin/';\n\t\t\t$config['source_image']\t= '/path/to/image/mypic.jpg';\n\t\t\t$config['x_axis'] = 100;\n\t\t\t$config['y_axis'] = 60;\n\n\t\t\t$this->image_lib->initialize($config); \n\n\t\t\tif ( ! $this->image_lib->crop())\n\t\t\t{\n\t\t\t\techo $this->image_lib->display_errors();\n\t\t\t}\n\n\t\t.. note:: Without a visual interface it is difficult to crop images, so this\n\t\t\tmethod is not very useful unless you intend to build such an\n\t\t\tinterface. That's exactly what we did using for the photo gallery module\n\t\t\tin ExpressionEngine, the CMS we develop. We added a JavaScript UI that\n\t\t\tlets the cropping area be selected. (from EllisLab)\n\n\t.. php:method:: rotate()\n\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tThe image rotation method requires that the angle of rotation be set\n\t\tvia its preference::\n\n\t\t\t$config['rotation_angle'] = '90';\n\n\t\tThere are 5 rotation options:\n\n\t\t#. 90 - rotates counter-clockwise by 90 degrees.\n\t\t#. 180 - rotates counter-clockwise by 180 degrees.\n\t\t#. 270 - rotates counter-clockwise by 270 degrees.\n\t\t#. hor - flips the image horizontally.\n\t\t#. vrt - flips the image vertically.\n\n\t\tHere's an example showing how you might rotate an image::\n\n\t\t\t$config['image_library'] = 'netpbm';\n\t\t\t$config['library_path'] = '/usr/bin/';\n\t\t\t$config['source_image']\t= '/path/to/image/mypic.jpg';\n\t\t\t$config['rotation_angle'] = 'hor';\n\n\t\t\t$this->image_lib->initialize($config); \n\n\t\t\tif ( ! $this->image_lib->rotate())\n\t\t\t{\n\t\t\t\techo $this->image_lib->display_errors();\n\t\t\t}\n\n\t.. php:method:: watermark()\n\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tCreates a watermark over an image, please refer to the :ref:`watermarking`\n\t\tsection for more info.\t\t\n\n\t.. php:method:: clear()\n\n\t\t:rtype:\tvoid\n\n\t\tThe clear method resets all of the values used when processing an\n\t\timage. You will want to call this if you are processing images in a\n\t\tloop.\n\n\t\t::\n\n\t\t\t$this->image_lib->clear();\n\n\t.. php:method:: display_errors([$open = '<p>[, $close = '</p>']])\n\n\t\t:param\tstring\t$open: Error message opening tag\n\t\t:param\tstring\t$close: Error message closing tag\n\t\t:returns:\tError messages\n\t\t:rtype:\tstring\n\n\t\tReturns all detected errors formatted as a string.\n\t\t::\n\n\t\t\techo $this->image_lib->display_errors();\n"
  },
  {
    "path": "user_guide_src/source/libraries/index.rst",
    "content": "#########\nLibraries\n#########\n\n.. toctree::\n\t:glob:\n\t:titlesonly:\n\t\n\t*"
  },
  {
    "path": "user_guide_src/source/libraries/input.rst",
    "content": "###########\nInput Class\n###########\n\nThe Input Class provides some helper methods for accessing input data\nand pre-processing it.\n\n.. note:: This class is initialized automatically by the system so there\n\tis no need to do it manually.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n********************\nAccessing input data\n********************\n\nUsing POST, GET, COOKIE, or SERVER Data\n=======================================\n\nCodeIgniter comes with helper methods that let you fetch POST, GET,\nCOOKIE or SERVER items. The main advantage of using the provided\nmethods rather than fetching an item directly (``$_POST['something']``)\nis that the methods will check to see if the item is set and return\nNULL if not. This lets you conveniently use data without\nhaving to test whether an item exists first. In other words, normally\nyou might do something like this::\n\n\t$something = isset($_POST['something']) ? $_POST['something'] : NULL;\n\nWith CodeIgniter's built in methods you can simply do this::\n\n\t$something = $this->input->post('something');\n\nThe main methods are:\n\n-  ``$this->input->post()``\n-  ``$this->input->get()``\n-  ``$this->input->cookie()``\n-  ``$this->input->server()``\n\nUsing the php://input stream\n============================\n\nIf you want to utilize the PUT, DELETE, PATCH or other exotic request\nmethods, they can only be accessed via a special input stream, that\ncan only be read once. This isn't as easy as just reading from e.g.\nthe ``$_POST`` array, because it will always exist and you can try\nand access multiple variables without caring that you might only have\none shot at all of the POST data.\n\nCodeIgniter will take care of that for you, and you can read the data\nfrom the **php://input** stream at any time, just by using the\n``$raw_input_stream`` property::\n\n\t$this->input->raw_input_stream;\n\nAdditionally if the input stream is form-encoded like $_POST you can \naccess its values by calling the\n``input_stream()`` method::\n\n\t$this->input->input_stream('key');\n\nSimilar to other methods such as ``get()`` and ``post()``, if the\nrequested data is not found, it will return NULL and you can also\ndecide whether to run the data through ``xss_clean()`` by passing\na boolean value as the second parameter::\n\n\t$this->input->input_stream('key', TRUE); // XSS Clean\n\t$this->input->input_stream('key', FALSE); // No XSS filter\n\n.. note:: You can utilize ``method()`` in order to know if you're reading\n\tPUT, DELETE or PATCH data.\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Input\n\n\t.. attribute:: $raw_input_stream\n\t\t\n\t\tRead only property that will return php://input data as is.\n\t\t\n\t\tThe property can be read multiple times.\n\n\t.. php:method:: post([$index = NULL[, $xss_clean = FALSE]])\n\n\t\t:param\tmixed\t$index: POST parameter name\n\t\t:param\tbool\t$xss_clean: Whether to apply XSS filtering\n\t\t:returns:\t$_POST if no parameters supplied, otherwise the POST value if found or NULL if not\n\t\t:rtype:\tmixed\n\n\t\tThe first parameter will contain the name of the POST item you are\n\t\tlooking for::\n\n\t\t\t$this->input->post('some_data');\n\n\t\tThe method returns NULL if the item you are attempting to retrieve\n\t\tdoes not exist.\n\n\t\tThe second optional parameter lets you run the data through the XSS\n\t\tfilter. It's enabled by setting the second parameter to boolean TRUE\n\t\t::\n\n\t\t\t$this->input->post('some_data', TRUE);\n\n\t\tTo return an array of all POST items call without any parameters.\n\n\t\tTo return all POST items and pass them through the XSS filter set the\n\t\tfirst parameter NULL while setting the second parameter to boolean TRUE.\n\t\t::\n\n\t\t\t$this->input->post(NULL, TRUE); // returns all POST items with XSS filter\n\t\t\t$this->input->post(NULL, FALSE); // returns all POST items without XSS filter\n\n\t\tTo return an array of multiple POST parameters, pass all the required keys\n\t\tas an array.\n\t\t::\n\n\t\t\t$this->input->post(array('field1', 'field2'));\n\n\t\tSame rule applied here, to retrieve the parameters with XSS filtering enabled, set the\n\t\tsecond parameter to boolean TRUE.\n\t\t::\n\n\t\t\t$this->input->post(array('field1', 'field2'), TRUE);\n\n\t.. php:method:: get([$index = NULL[, $xss_clean = FALSE]])\n\n\t\t:param\tmixed\t$index: GET parameter name\n\t\t:param\tbool\t$xss_clean: Whether to apply XSS filtering\n\t\t:returns:\t$_GET if no parameters supplied, otherwise the GET value if found or NULL if not\n\t\t:rtype:\tmixed\n\n\t\tThis method is identical to ``post()``, only it fetches GET data.\n\t\t::\n\n\t\t\t$this->input->get('some_data', TRUE);\n\n\t\tTo return an array of all GET items call without any parameters.\n\n\t\tTo return all GET items and pass them through the XSS filter set the\n\t\tfirst parameter NULL while setting the second parameter to boolean TRUE.\n\t\t::\n\n\t\t\t$this->input->get(NULL, TRUE); // returns all GET items with XSS filter\n\t\t\t$this->input->get(NULL, FALSE); // returns all GET items without XSS filtering\n\n\t\tTo return an array of multiple GET parameters, pass all the required keys\n\t\tas an array.\n\t\t::\n\n\t\t\t$this->input->get(array('field1', 'field2'));\n\n\t\tSame rule applied here, to retrieve the parameters with XSS filtering enabled, set the\n\t\tsecond parameter to boolean TRUE.\n\t\t::\n\n\t\t\t$this->input->get(array('field1', 'field2'), TRUE);\n\n\t.. php:method:: post_get($index[, $xss_clean = FALSE])\n\n\t\t:param\tstring\t$index: POST/GET parameter name\n\t\t:param\tbool\t$xss_clean: Whether to apply XSS filtering\n\t\t:returns:\tPOST/GET value if found, NULL if not\n\t\t:rtype:\tmixed\n\n\t\tThis method works pretty much the same way as ``post()`` and ``get()``,\n\t\tonly combined. It will search through both POST and GET streams for data,\n\t\tlooking in POST first, and then in GET::\n\n\t\t\t$this->input->post_get('some_data', TRUE);\n\n\t.. php:method:: get_post($index[, $xss_clean = FALSE])\n\n\t\t:param\tstring\t$index: GET/POST parameter name\n\t\t:param\tbool\t$xss_clean: Whether to apply XSS filtering\n\t\t:returns:\tGET/POST value if found, NULL if not\n\t\t:rtype:\tmixed\n\n\t\tThis method works the same way as ``post_get()`` only it looks for GET\n\t\tdata first.\n\n\t\t\t$this->input->get_post('some_data', TRUE);\n\n\t\t.. note:: This method used to act EXACTLY like ``post_get()``, but it's\n\t\t\tbehavior has changed in CodeIgniter 3.0.\n\n\t.. php:method:: cookie([$index = NULL[, $xss_clean = FALSE]])\n\n\t\t:param\tmixed\t$index: COOKIE name\n\t\t:param\tbool\t$xss_clean: Whether to apply XSS filtering\n\t\t:returns:\t$_COOKIE if no parameters supplied, otherwise the COOKIE value if found or NULL if not\n\t\t:rtype:\tmixed\n\n\t\tThis method is identical to ``post()`` and ``get()``, only it fetches cookie\n\t\tdata::\n\n\t\t\t$this->input->cookie('some_cookie');\n\t\t\t$this->input->cookie('some_cookie', TRUE); // with XSS filter\n\n\t\tTo return an array of multiple cookie values, pass all the required keys\n\t\tas an array.\n\t\t::\n\n\t\t\t$this->input->cookie(array('some_cookie', 'some_cookie2'));\n\n\t\t.. note:: Unlike the :doc:`Cookie Helper <../helpers/cookie_helper>`\n\t\t\tfunction :php:func:`get_cookie()`, this method does NOT prepend\n\t\t\tyour configured ``$config['cookie_prefix']`` value.\n\n\t.. php:method:: server($index[, $xss_clean = FALSE])\n\n\t\t:param\tmixed\t$index: Value name\n\t\t:param\tbool\t$xss_clean: Whether to apply XSS filtering\n\t\t:returns:\t$_SERVER item value if found, NULL if not\n\t\t:rtype:\tmixed\n\n\t\tThis method is identical to the ``post()``, ``get()`` and ``cookie()``\n\t\tmethods, only it fetches server data (``$_SERVER``)::\n\n\t\t\t$this->input->server('some_data');\n\n\t\tTo return an array of multiple ``$_SERVER`` values, pass all the required keys\n\t\tas an array.\n\t\t::\n\n\t\t\t$this->input->server(array('SERVER_PROTOCOL', 'REQUEST_URI'));\n\n\t.. php:method:: input_stream([$index = NULL[, $xss_clean = FALSE]])\n\n\t\t:param\tmixed\t$index: Key name\n\t\t:param\tbool\t$xss_clean: Whether to apply XSS filtering\n\t\t:returns:\tInput stream array if no parameters supplied, otherwise the specified value if found or NULL if not\n\t\t:rtype:\tmixed\n\n\t\tThis method is identical to ``get()``, ``post()`` and ``cookie()``,\n\t\tonly it fetches the *php://input* stream data.\n\n\t.. php:method:: set_cookie($name = ''[, $value = ''[, $expire = 0[, $domain = ''[, $path = '/'[, $prefix = ''[, $secure = NULL[, $httponly = NULL[, $samesite = NULL]]]]]]]])\n\n\t\t:param\tmixed\t$name: Cookie name or an array of parameters\n\t\t:param\tstring\t$value: Cookie value\n\t\t:param\tint\t$expire: Cookie expiration time in seconds\n\t\t:param\tstring\t$domain: Cookie domain\n\t\t:param\tstring\t$path: Cookie path\n\t\t:param\tstring\t$prefix: Cookie name prefix\n\t\t:param\tbool\t$secure: Whether to only transfer the cookie through HTTPS\n\t\t:param\tbool\t$httponly: Whether to only make the cookie accessible for HTTP requests (no JavaScript)\n\t\t:param\tstring\t$samesite: SameSite attribute ('Lax', 'Strict', 'None')\n\t\t:rtype:\tvoid\n\n\n\t\tSets a cookie containing the values you specify. There are two ways to\n\t\tpass information to this method so that a cookie can be set: Array\n\t\tMethod, and Discrete Parameters:\n\n\t\t**Array Method**\n\n\t\tUsing this method, an associative array is passed to the first\n\t\tparameter::\n\n\t\t\t$cookie = array(\n\t\t\t\t'name'\t\t=> 'The Cookie Name',\n\t\t\t\t'value'\t\t=> 'The Value',\n\t\t\t\t'expire'\t=> 86500,\n\t\t\t\t'domain'\t=> '.some-domain.com',\n\t\t\t\t'path'\t\t=> '/',\n\t\t\t\t'prefix'\t=> 'myprefix_',\n\t\t\t\t'secure'\t=> TRUE,\n\t\t\t\t'samesite'\t=> 'Strict'\n\t\t\t);\n\n\t\t\t$this->input->set_cookie($cookie);\n\n\t\t**Notes**\n\n\t\tOnly the name and value are required. To delete a cookie set the expiry\n\t\ttime to a negative, or non-numeric value.\n\n\t\tThe expiration is set in **seconds**, which will be added to the current\n\t\ttime. Do not include the time, but rather only the number of seconds\n\t\tfrom *now* that you wish the cookie to be valid. If the expiration is\n\t\tset to zero the cookie will only last as long as the browser is open.\n\n\t\tFor site-wide cookies regardless of how your site is requested, add your\n\t\tURL to the **domain** starting with a period, like this:\n\t\t.your-domain.com\n\n\t\tThe path is usually not needed since the method sets a root path.\n\n\t\tThe prefix is only needed if you need to avoid name collisions with\n\t\tother identically named cookies for your server.\n\n\t\tThe *httponly* and *secure* flags, when omitted, will default to your\n\t\t``$config['cookie_httponly']`` and ``$config['cookie_secure']`` settings.\n\t\tThe *samesite* parameter can be ``'Lax'``, ``'Strict'`` or ``'None'``. If not set, the same-site cookie attribute will default to ``'Lax'``.\n\n\t\t**Discrete Parameters**\n\n\t\tIf you prefer, you can set the cookie by passing data using individual\n\t\tparameters::\n\n\t\t\t$this->input->set_cookie($name, $value, $expire, $domain, $path, $prefix, $secure, $samesite);\n\n\t.. php:method:: ip_address()\n\n\t\t:returns:\tVisitor's IP address or '0.0.0.0' if not valid\n\t\t:rtype:\tstring\n\n\t\tReturns the IP address for the current user. If the IP address is not\n\t\tvalid, the method will return '0.0.0.0'::\n\n\t\t\techo $this->input->ip_address();\n\n\t\t.. important:: This method takes into account the ``$config['proxy_ips']``\n\t\t\tsetting and will return the reported HTTP_X_FORWARDED_FOR,\n\t\t\tHTTP_CLIENT_IP, HTTP_X_CLIENT_IP or HTTP_X_CLUSTER_CLIENT_IP\n\t\t\taddress for the allowed IP addresses.\n\n\t.. php:method:: valid_ip($ip[, $which = ''])\n\n\t\t:param\tstring\t$ip: IP address\n\t\t:param\tstring\t$which: IP protocol ('ipv4' or 'ipv6')\n\t\t:returns:\tTRUE if the address is valid, FALSE if not\n\t\t:rtype:\tbool\n\n\t\tTakes an IP address as input and returns TRUE or FALSE (boolean) depending\n\t\ton whether it is valid or not.\n\n\t\t.. note:: The $this->input->ip_address() method above automatically\n\t\t\tvalidates the IP address.\n\n\t\t::\n\n\t\t\tif ( ! $this->input->valid_ip($ip))\n\t\t\t{\n\t\t\t\techo 'Not Valid';\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\techo 'Valid';\n\t\t\t}\n\n\t\tAccepts an optional second string parameter of 'ipv4' or 'ipv6' to specify\n\t\tan IP format. The default checks for both formats.\n\n\t.. php:method:: user_agent([$xss_clean = FALSE])\n\n\t\t:returns:\tUser agent string or NULL if not set\n\t\t:param\tbool\t$xss_clean: Whether to apply XSS filtering\n\t\t:rtype:\tmixed\n\n\t\tReturns the user agent string (web browser) being used by the current user,\n\t\tor NULL if it's not available.\n\t\t::\n\n\t\t\techo $this->input->user_agent();\n\n\t\tSee the :doc:`User Agent Class <user_agent>` for methods which extract\n\t\tinformation from the user agent string.\n\n\t.. php:method:: request_headers([$xss_clean = FALSE])\n\n\t\t:param\tbool\t$xss_clean: Whether to apply XSS filtering\n\t\t:returns:\tAn array of HTTP request headers\n\t\t:rtype:\tarray\n\n\t\tReturns an array of HTTP request headers.\n\t\tUseful if running in a non-Apache environment where\n\t\t`apache_request_headers() <https://secure.php.net/apache_request_headers>`_\n\t\twill not be supported.\n\t\t::\n\n\t\t\t$headers = $this->input->request_headers();\n\n\t.. php:method:: get_request_header($index[, $xss_clean = FALSE])\n\n\t\t:param\tstring\t$index: HTTP request header name\n\t\t:param\tbool\t$xss_clean: Whether to apply XSS filtering\n\t\t:returns:\tAn HTTP request header or NULL if not found\n\t\t:rtype:\tstring\n\n\t\tReturns a single member of the request headers array or NULL\n\t\tif the searched header is not found.\n\t\t::\n\n\t\t\t$this->input->get_request_header('some-header', TRUE);\n\n\t.. php:method:: is_ajax_request()\n\n\t\t:returns:\tTRUE if it is an Ajax request, FALSE if not\n\t\t:rtype:\tbool\n\n\t\tChecks to see if the HTTP_X_REQUESTED_WITH server header has been\n\t\tset, and returns boolean TRUE if it is or FALSE if not.\n\n\t.. php:method:: method([$upper = FALSE])\n\n\t\t:param\tbool\t$upper: Whether to return the request method name in upper or lower case\n\t\t:returns:\tHTTP request method\n\t\t:rtype:\tstring\n\n\t\tReturns the ``$_SERVER['REQUEST_METHOD']``, with the option to set it\n\t\tin uppercase or lowercase.\n\t\t::\n\n\t\t\techo $this->input->method(TRUE); // Outputs: POST\n\t\t\techo $this->input->method(FALSE); // Outputs: post\n\t\t\techo $this->input->method(); // Outputs: post\n"
  },
  {
    "path": "user_guide_src/source/libraries/language.rst",
    "content": "##############\nLanguage Class\n##############\n\nThe Language Class provides functions to retrieve language files and\nlines of text for purposes of internationalization.\n\nIn your CodeIgniter **system** folder, you will find a **language** sub-directory\ncontaining a set of language files for the **english** idiom.\nThe files in this directory (**system/language/english/**) define the regular messages,\nerror messages, and other generally output terms or expressions, for the different parts\nof the CodeIgniter framework.\n\nYou can create or incorporate your own language files, as needed, in order to provide\napplication-specific error and other messages, or to provide translations of the core\nmessages into other languages. These translations or additional messages would go inside\nyour **application/language/** directory, with separate sub-directories for each idiom\n(for instance, 'french' or 'german').\n\nThe CodeIgniter framework comes with a set of language files for the \"english\" idiom.\nAdditional approved translations for different idioms may be found in the\n`CodeIgniter 3 Translations repositories <https://github.com/bcit-ci/codeigniter3-translations>`_.\nEach repository deals with a single idiom.\n\nWhen CodeIgniter loads language files, it will load the one in **system/language/**\nfirst and will then look for an override in your **application/language/** directory.\n\n.. note:: Each language should be stored in its own folder. For example,\n\tthe English files are located at: system/language/english\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n***************************\nHandling Multiple Languages\n***************************\n\nIf you want to support multiple languages in your application, you would provide folders inside\nyour **application/language/** directory for each of them, and you would specify the default\nlanguage in your **application/config/config.php**.\n\nThe **application/language/english/** directory would contain any additional language files\nneeded by your application, for instance for error messages.\n\nEach of the other idiom-specific directories would contain the core language files that you\nobtained from the translations repositories, or that you translated yourself, as well as\nany additional ones needed by your application.\n\nYou would store the language you are currently using, for instance in a session variable.\n\nSample Language Files\n=====================\n\n::\n\n\tsystem/\n\t\tlanguage/\n\t\t\tenglish/\n\t\t\t\t...\n\t\t\t\temail_lang.php\n\t\t\t\tform_validation_lang.php\n\t\t\t\t...\n\n\tapplication/\n\t\tlanguage/\n\t\t\tenglish/\n\t\t\t\terror_messages_lang.php\n\t\t\tfrench/\n\t\t\t\t...\n\t\t\t\temail_lang.php\n\t\t\t\terror_messages_lang.php\n\t\t\t\tform_validation_lang.php\n\t\t\t\t...\n\nExample of switching languages\n==============================\n\n::\n\n\t$idiom = $this->session->get_userdata('language');\n\t$this->lang->load('error_messages', $idiom);\n\t$oops = $this->lang->line('message_key');\n\n********************\nInternationalization\n********************\n\nThe Language class in CodeIgniter is meant to provide an easy and lightweight\nway to support multiplelanguages in your application. It is not meant to be a\nfull implementation of what is commonly called `internationalization and localization\n<https://en.wikipedia.org/wiki/Internationalization_and_localization>`_.\n\nWe use the term \"idiom\" to refer to a language using its common name,\nrather than using any of the international standards, such as \"en\", \"en-US\",\nor \"en-CA-x-ca\" for English and some of its variants.\n\n.. note:: There is nothing to prevent you from using those abbreviations in your application!\n\n************************\nUsing the Language Class\n************************\n\nCreating Language Files\n=======================\n\nLanguage files must be named with **_lang.php** as the filename extension.\nFor example, let's say you want to create a file containing error messages.\nYou might name it: error_lang.php\n\nWithin the file you will assign each line of text to an array called\n``$lang`` with this prototype::\n\n\t$lang['language_key'] = 'The actual message to be shown';\n\n.. note:: It's a good practice to use a common prefix for all messages\n\tin a given file to avoid collisions with similarly named items in other\n\tfiles. For example, if you are creating error messages you might prefix\n\tthem with error\\_\n\n::\n\n\t$lang['error_email_missing'] = 'You must submit an email address';\n\t$lang['error_url_missing'] = 'You must submit a URL';\n\t$lang['error_username_missing'] = 'You must submit a username';\n\nLoading A Language File\n=======================\n\nIn order to fetch a line from a particular file you must load the file\nfirst. Loading a language file is done with the following code::\n\n\t$this->lang->load('filename', 'language');\n\nWhere filename is the name of the file you wish to load (without the\nfile extension), and language is the language set containing it (ie,\nenglish). If the second parameter is missing, the default language set\nin your **application/config/config.php** file will be used.\n\nYou can also load multiple language files at the same time by passing an array of language files as first parameter.\n::\n\n\t$this->lang->load(array('filename1', 'filename2'));\n\n.. note:: The *language* parameter can only consist of letters.\n\nFetching a Line of Text\n=======================\n\nOnce your desired language file is loaded you can access any line of\ntext using this function::\n\n\t$this->lang->line('language_key');\n\nWhere *language_key* is the array key corresponding to the line you wish\nto show.\n\nYou can optionally pass FALSE as the second argument of that method to\ndisable error logging, in case you're not sure if the line exists::\n\n\t$this->lang->line('misc_key', FALSE);\n\n.. note:: This method simply returns the line. It does not echo it.\n\nUsing language lines as form labels\n-----------------------------------\n\nThis feature has been deprecated from the language library and moved to\nthe :php:func:`lang()` function of the :doc:`Language Helper\n<../helpers/language_helper>`.\n\nAuto-loading Languages\n======================\n\nIf you find that you need a particular language globally throughout your\napplication, you can tell CodeIgniter to :doc:`auto-load\n<../general/autoloader>` it during system initialization. This is done\nby opening the **application/config/autoload.php** file and adding the\nlanguage(s) to the autoload array.\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Lang\n\n\t.. php:method:: load($langfile[, $idiom = ''[, $return = FALSE[, $add_suffix = TRUE[, $alt_path = '']]]])\n\n\t\t:param\tmixed\t$langfile: Language file to load or array with multiple files\n\t\t:param\tstring\t$idiom: Language name (i.e. 'english')\n\t\t:param\tbool\t$return: Whether to return the loaded array of translations\n\t\t:param\tbool\t$add_suffix: Whether to add the '_lang' suffix to the language file name\n\t\t:param\tstring\t$alt_path: An alternative path to look in for the language file\n\t\t:returns:\tArray of language lines if $return is set to TRUE, otherwise void\n\t\t:rtype:\tmixed\n\n\t\tLoads a language file.\n\n\t.. php:method:: line($line[, $log_errors = TRUE])\n\n\t\t:param\tstring\t$line: Language line key name\n\t\t:param\tbool\t$log_errors: Whether to log an error if the line isn't found\n\t\t:returns:\tLanguage line string or FALSE on failure\n\t\t:rtype:\tstring\n\n\t\tFetches a single translation line from the already loaded language files,\n\t\tbased on the line's name."
  },
  {
    "path": "user_guide_src/source/libraries/loader.rst",
    "content": "############\nLoader Class\n############\n\nLoader, as the name suggests, is used to load elements. These elements\ncan be libraries (classes) :doc:`View files <../general/views>`,\n:doc:`Drivers <../general/drivers>`,\n:doc:`Helpers <../general/helpers>`,\n:doc:`Models <../general/models>`, or your own files.\n\n.. note:: This class is initialized automatically by the system so there\n\tis no need to do it manually.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n**********************\nApplication \"Packages\"\n**********************\n\nAn application package allows for the easy distribution of complete sets\nof resources in a single directory, complete with its own libraries,\nmodels, helpers, config, and language files. It is recommended that\nthese packages be placed in the application/third_party directory. Below\nis a sample map of an package directory.\n\nThe following is an example of a directory for an application package\nnamed \"Foo Bar\".\n\n::\n\n\t/application/third_party/foo_bar\n\n\tconfig/\n\thelpers/\n\tlanguage/\n\tlibraries/\n\tmodels/\n\nWhatever the purpose of the \"Foo Bar\" application package, it has its\nown config files, helpers, language files, libraries, and models. To use\nthese resources in your controllers, you first need to tell the Loader\nthat you are going to be loading resources from a package, by adding the\npackage path via the ``add_package_path()`` method.\n\nPackage view files\n------------------\n\nBy Default, package view files paths are set when ``add_package_path()``\nis called. View paths are looped through, and once a match is\nencountered that view is loaded.\n\nIn this instance, it is possible for view naming collisions within\npackages to occur, and possibly the incorrect package being loaded. To\nensure against this, set an optional second parameter of FALSE when\ncalling ``add_package_path()``.\n\n::\n\n\t$this->load->add_package_path(APPPATH.'my_app', FALSE);\n\t$this->load->view('my_app_index'); // Loads\n\t$this->load->view('welcome_message'); // Will not load the default welcome_message b/c the second param to add_package_path is FALSE\n\n\t// Reset things\n\t$this->load->remove_package_path(APPPATH.'my_app');\n\n\t// Again without the second parameter:\n\t$this->load->add_package_path(APPPATH.'my_app');\n\t$this->load->view('my_app_index'); // Loads\n\t$this->load->view('welcome_message'); // Loads\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Loader\n\n\t.. php:method:: library($library[, $params = NULL[, $object_name = NULL]])\n\n\t\t:param\tmixed\t$library: Library name as a string or an array with multiple libraries\n\t\t:param\tarray\t$params: Optional array of parameters to pass to the loaded library's constructor\n\t\t:param\tstring\t$object_name: Optional object name to assign the library to\n\t\t:returns:\tCI_Loader instance (method chaining)\n\t\t:rtype:\tCI_Loader\n\n\t\tThis method is used to load core classes.\n\n\t\t.. note:: We use the terms \"class\" and \"library\" interchangeably.\n\n\t\tFor example, if you would like to send email with CodeIgniter, the first\n\t\tstep is to load the email class within your controller::\n\n\t\t\t$this->load->library('email');\n\n\t\tOnce loaded, the library will be ready for use, using ``$this->email``.\n\n\t\tLibrary files can be stored in subdirectories within the main\n\t\t\"libraries\" directory, or within your personal *application/libraries*\n\t\tdirectory. To load a file located in a subdirectory, simply include the\n\t\tpath, relative to the \"libraries\" directory. For example, if you have\n\t\tfile located at::\n\n\t\t\tlibraries/flavors/Chocolate.php\n\n\t\tYou will load it using::\n\n\t\t\t$this->load->library('flavors/chocolate');\n\n\t\tYou may nest the file in as many subdirectories as you want.\n\n\t\tAdditionally, multiple libraries can be loaded at the same time by\n\t\tpassing an array of libraries to the load method.\n\t\t::\n\n\t\t\t$this->load->library(array('email', 'table'));\n\n\t\t**Setting options**\n\n\t\tThe second (optional) parameter allows you to optionally pass\n\t\tconfiguration setting. You will typically pass these as an array::\n\n\t\t\t$config = array (\n\t\t\t\t'mailtype' => 'html',\n\t\t\t\t'charset'  => 'utf-8',\n\t\t\t\t'priority' => '1'\n\t\t\t);\n\n\t\t\t$this->load->library('email', $config);\n\n\t\tConfig options can usually also be set via a config file. Each library\n\t\tis explained in detail in its own page, so please read the information\n\t\tregarding each one you would like to use.\n\n\t\tPlease take note, when multiple libraries are supplied in an array for\n\t\tthe first parameter, each will receive the same parameter information.\n\n\t\t**Assigning a Library to a different object name**\n\n\t\tIf the third (optional) parameter is blank, the library will usually be\n\t\tassigned to an object with the same name as the library. For example, if\n\t\tthe library is named Calendar, it will be assigned to a variable named\n\t\t``$this->calendar``.\n\n\t\tIf you prefer to set your own class names you can pass its value to the\n\t\tthird parameter::\n\n\t\t\t$this->load->library('calendar', NULL, 'my_calendar');\n\n\t\t\t// Calendar class is now accessed using:\n\t\t\t$this->my_calendar\n\n\t\tPlease take note, when multiple libraries are supplied in an array for\n\t\tthe first parameter, this parameter is discarded.\n\n\t.. php:method:: driver($library[, $params = NULL[, $object_name]])\n\n\t\t:param\tmixed\t$library: Library name as a string or an array with multiple libraries\n\t\t:param\tarray\t$params: Optional array of parameters to pass to the loaded library's constructor\n\t\t:param\tstring\t$object_name: Optional object name to assign the library to\n\t\t:returns:\tCI_Loader instance (method chaining)\n\t\t:rtype:\tCI_Loader\n\n\t\tThis method is used to load driver libraries, acts very much like the\n\t\t``library()`` method.\n\n\t\tAs an example, if you would like to use sessions with CodeIgniter, the first\n\t\tstep is to load the session driver within your controller::\n\n\t\t\t$this->load->driver('session');\n\n\t\tOnce loaded, the library will be ready for use, using ``$this->session``.\n\n\t\tDriver files must be stored in a subdirectory within the main\n\t\t\"libraries\" directory, or within your personal *application/libraries*\n\t\tdirectory. The subdirectory must match the parent class name. Read the\n\t\t:doc:`Drivers <../general/drivers>` description for details.\n\n\t\tAdditionally, multiple driver libraries can be loaded at the same time by\n\t\tpassing an array of drivers to the load method.\n\t\t::\n\n\t\t\t$this->load->driver(array('session', 'cache'));\n\n\t\t**Setting options**\n\n\t\tThe second (optional) parameter allows you to optionally pass\n\t\tconfiguration settings. You will typically pass these as an array::\n\n\t\t\t$config = array(\n\t\t\t\t'sess_driver' => 'cookie',\n\t\t\t\t'sess_encrypt_cookie'  => true,\n\t\t\t\t'encryption_key' => 'mysecretkey'\n\t\t\t);\n\n\t\t\t$this->load->driver('session', $config);\n\n\t\tConfig options can usually also be set via a config file. Each library\n\t\tis explained in detail in its own page, so please read the information\n\t\tregarding each one you would like to use.\n\n\t\t**Assigning a Driver to a different object name**\n\n\t\tIf the third (optional) parameter is blank, the library will be assigned\n\t\tto an object with the same name as the parent class. For example, if\n\t\tthe library is named Session, it will be assigned to a variable named\n\t\t``$this->session``.\n\n\t\tIf you prefer to set your own class names you can pass its value to the\n\t\tthird parameter::\n\n\t\t\t$this->load->library('session', '', 'my_session');\n\n\t\t\t// Session class is now accessed using:\n\t\t\t$this->my_session\n\n\t.. php:method:: view($view[, $vars = array()[, return = FALSE]])\n\n\t\t:param\tstring\t$view: View name\n\t\t:param\tarray\t$vars: An associative array of variables\n\t\t:param\tbool\t$return: Whether to return the loaded view\n\t\t:returns:\tView content string if $return is set to TRUE, otherwise CI_Loader instance (method chaining)\n\t\t:rtype:\tmixed\n\n\t\tThis method is used to load your View files. If you haven't read the\n\t\t:doc:`Views <../general/views>` section of the user guide it is\n\t\trecommended that you do since it shows you how this method is\n\t\ttypically used.\n\n\t\tThe first parameter is required. It is the name of the view file you\n\t\twould like to load.\n\n\t\t.. note:: The .php file extension does not need to be specified unless\n\t\t\tyou use something other than .php.\n\n\t\tThe second **optional** parameter can take an associative array or an\n\t\tobject as input, which it runs through the PHP\n\t\t`extract() <https://secure.php.net/extract>`_ function to convert to variables\n\t\tthat can be used in your view files. Again, read the\n\t\t:doc:`Views <../general/views>` page to learn how this might be useful.\n\n\t\tThe third **optional** parameter lets you change the behavior of the\n\t\tmethod so that it returns data as a string rather than sending it to\n\t\tyour browser. This can be useful if you want to process the data in some\n\t\tway. If you set the parameter to TRUE (boolean) it will return data. The\n\t\tdefault behavior is FALSE, which sends it to your browser. Remember to\n\t\tassign it to a variable if you want the data returned::\n\n\t\t\t$string = $this->load->view('myfile', '', TRUE);\n\n\t.. php:method:: vars($vars[, $val = ''])\n\n\t\t:param\tmixed\t$vars: An array of variables or a single variable name\n\t\t:param\tmixed\t$val: Optional variable value\n\t\t:returns:\tCI_Loader instance (method chaining)\n\t\t:rtype:\tCI_Loader\n\n\t\tThis method takes an associative array as input and generates\n\t\tvariables using the PHP `extract() <https://secure.php.net/extract>`_\n\t\tfunction. This method produces the same result as using the second\n\t\tparameter of the ``$this->load->view()`` method above. The reason you\n\t\tmight want to use this method independently is if you would like to\n\t\tset some global variables in the constructor of your controller and have\n\t\tthem become available in any view file loaded from any method. You can\n\t\thave multiple calls to this method. The data get cached and merged\n\t\tinto one array for conversion to variables.\n\n\t.. php:method:: get_var($key)\n\n\t\t:param\tstring\t$key: Variable name key\n\t\t:returns:\tValue if key is found, NULL if not\n\t\t:rtype:\tmixed\n\n\t\tThis method checks the associative array of variables available to\n\t\tyour views. This is useful if for any reason a var is set in a library\n\t\tor another controller method using ``$this->load->vars()``.\n\n\t.. php:method:: get_vars()\n\n\t\t:returns:\tAn array of all assigned view variables\n\t\t:rtype:\tarray\n\n\t\tThis method retrieves all variables available to your views.\n\n\t.. php:method:: clear_vars()\n\n\t\t:returns:\tCI_Loader instance (method chaining)\n\t\t:rtype:\tCI_Loader\n\n\t\tClears cached view variables.\n\n\t.. php:method:: model($model[, $name = ''[, $db_conn = FALSE]])\n\n\t\t:param\tmixed\t$model: Model name or an array containing multiple models\n\t\t:param\tstring\t$name: Optional object name to assign the model to\n\t\t:param\tstring\t$db_conn: Optional database configuration group to load\n\t\t:returns:\tCI_Loader instance (method chaining)\n\t\t:rtype:\tCI_Loader\n\n\t\t::\n\n\t\t\t$this->load->model('model_name');\n\n\n\t\tIf your model is located in a subdirectory, include the relative path\n\t\tfrom your models directory. For example, if you have a model located at\n\t\t*application/models/blog/Queries.php* you'll load it using::\n\n\t\t\t$this->load->model('blog/queries');\n\n\t\tIf you would like your model assigned to a different object name you can\n\t\tspecify it via the second parameter of the loading method::\n\n\t\t\t$this->load->model('model_name', 'fubar');\n\t\t\t$this->fubar->method();\n\n\t.. php:method:: database([$params = ''[, $return = FALSE[, $query_builder = NULL]]])\n\n\t\t:param\tmixed\t$params: Database group name or configuration options\n\t\t:param\tbool\t$return: Whether to return the loaded database object\n\t\t:param\tbool\t$query_builder: Whether to load the Query Builder\n\t\t:returns:\tLoaded CI_DB instance or FALSE on failure if $return is set to TRUE, otherwise CI_Loader instance (method chaining)\n\t\t:rtype:\tmixed\n\n\t\tThis method lets you load the database class. The two parameters are\n\t\t**optional**. Please see the :doc:`database <../database/index>`\n\t\tsection for more info.\n\n\t.. php:method:: dbforge([$db = NULL[, $return = FALSE]])\n\n\t\t:param\tobject\t$db: Database object\n\t\t:param\tbool\t$return: Whether to return the Database Forge instance\n\t\t:returns:\tLoaded CI_DB_forge instance if $return is set to TRUE, otherwise CI_Loader instance (method chaining)\n\t\t:rtype:\tmixed\n\n\t\tLoads the :doc:`Database Forge <../database/forge>` class, please refer\n\t\tto that manual for more info.\n\n\t.. php:method:: dbutil([$db = NULL[, $return = FALSE]])\n\n\t\t:param\tobject\t$db: Database object\n\t\t:param\tbool\t$return: Whether to return the Database Utilities instance\n\t\t:returns:\tLoaded CI_DB_utility instance if $return is set to TRUE, otherwise CI_Loader instance (method chaining)\n\t\t:rtype:\tmixed\n\n\t\tLoads the :doc:`Database Utilities <../database/utilities>` class, please\n\t\trefer to that manual for more info.\n\n\t.. php:method:: helper($helpers)\n\n\t\t:param\tmixed\t$helpers: Helper name as a string or an array containing multiple helpers\n\t\t:returns:\tCI_Loader instance (method chaining)\n\t\t:rtype:\tCI_Loader\n\n\t\tThis method loads helper files, where file_name is the name of the\n\t\tfile, without the _helper.php extension.\n\n\t.. php:method:: file($path[, $return = FALSE])\n\n\t\t:param\tstring\t$path: File path\n\t\t:param\tbool\t$return: Whether to return the loaded file\n\t\t:returns:\tFile contents if $return is set to TRUE, otherwise CI_Loader instance (method chaining)\n\t\t:rtype:\tmixed\n\n\t\tThis is a generic file loading method. Supply the filepath and name in\n\t\tthe first parameter and it will open and read the file. By default the\n\t\tdata is sent to your browser, just like a View file, but if you set the\n\t\tsecond parameter to boolean TRUE it will instead return the data as a\n\t\tstring.\n\n\t.. php:method:: language($files[, $lang = ''])\n\n\t\t:param\tmixed\t$files: Language file name or an array of multiple language files\n\t\t:param\tstring\t$lang: Language name\n\t\t:returns:\tCI_Loader instance (method chaining)\n\t\t:rtype:\tCI_Loader\n\n\t\tThis method is an alias of the :doc:`language loading\n\t\tmethod <language>`: ``$this->lang->load()``.\n\n\t.. php:method:: config($file[, $use_sections = FALSE[, $fail_gracefully = FALSE]])\n\n\t\t:param\tstring\t$file: Configuration file name\n\t\t:param\tbool\t$use_sections: Whether configuration values should be loaded into their own section\n\t\t:param\tbool\t$fail_gracefully: Whether to just return FALSE in case of failure\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tThis method is an alias of the :doc:`config file loading\n\t\tmethod <config>`: ``$this->config->load()``\n\n\t.. php:method:: is_loaded($class)\n\n\t\t:param\tstring\t$class: Class name\n\t\t:returns:\tSingleton property name if found, FALSE if not\n\t\t:rtype:\tmixed\n\n\t\tAllows you to check if a class has already been loaded or not.\n\n\t\t.. note:: The word \"class\" here refers to libraries and drivers.\n\n\t\tIf the requested class has been loaded, the method returns its assigned\n\t\tname in the CI Super-object and FALSE if it's not::\n\n\t\t\t$this->load->library('form_validation');\n\t\t\t$this->load->is_loaded('Form_validation');\t// returns 'form_validation'\n\n\t\t\t$this->load->is_loaded('Nonexistent_library');\t// returns FALSE\n\n\t\t.. important:: If you have more than one instance of a class (assigned to\n\t\t\tdifferent properties), then the first one will be returned.\n\n\t\t::\n\n\t\t\t$this->load->library('form_validation', $config, 'fv');\n\t\t\t$this->load->library('form_validation');\n\n\t\t\t$this->load->is_loaded('Form_validation');\t// returns 'fv'\n\n\t.. php:method:: add_package_path($path[, $view_cascade = TRUE])\n\n\t\t:param\tstring\t$path: Path to add\n\t\t:param\tbool\t$view_cascade: Whether to use cascading views\n\t\t:returns:\tCI_Loader instance (method chaining)\n\t\t:rtype:\tCI_Loader\n\n\t\tAdding a package path instructs the Loader class to prepend a given path\n\t\tfor subsequent requests for resources. As an example, the \"Foo Bar\"\n\t\tapplication package above has a library named Foo_bar.php. In our\n\t\tcontroller, we'd do the following::\n\n\t\t\t$this->load->add_package_path(APPPATH.'third_party/foo_bar/')\n\t\t\t\t->library('foo_bar');\n\n\t.. php:method:: remove_package_path([$path = ''])\n\n\t\t:param\tstring\t$path: Path to remove\n\t\t:returns:\tCI_Loader instance (method chaining)\n\t\t:rtype:\tCI_Loader\n\n\t\tWhen your controller is finished using resources from an application\n\t\tpackage, and particularly if you have other application packages you\n\t\twant to work with, you may wish to remove the package path so the Loader\n\t\tno longer looks in that directory for resources. To remove the last path\n\t\tadded, simply call the method with no parameters.\n\n\t\tOr to remove a specific package path, specify the same path previously\n\t\tgiven to ``add_package_path()`` for a package.::\n\n\t\t\t$this->load->remove_package_path(APPPATH.'third_party/foo_bar/');\n\n\t.. php:method:: get_package_paths([$include_base = TRUE])\n\n\t\t:param\tbool\t$include_base: Whether to include BASEPATH\n\t\t:returns:\tAn array of package paths\n\t\t:rtype:\tarray\n\n\t\tReturns all currently available package paths."
  },
  {
    "path": "user_guide_src/source/libraries/migration.rst",
    "content": "################\nMigrations Class\n################\n\nMigrations are a convenient way for you to alter your database in a \nstructured and organized manner. You could edit fragments of SQL by hand \nbut you would then be responsible for telling other developers that they \nneed to go and run them. You would also have to keep track of which changes \nneed to be run against the production machines next time you deploy.\n\nThe database table **migration** tracks which migrations have already been \nrun so all you have to do is update your application files and \ncall ``$this->migration->current()`` to work out which migrations should be run. \nThe current version is found in **application/config/migration.php**.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n********************\nMigration file names\n********************\n\nEach Migration is run in numeric order forward or backwards depending on the\nmethod taken. Two numbering styles are available:\n\n* **Sequential:** each migration is numbered in sequence, starting with **001**.\n  Each number must be three digits, and there must not be any gaps in the\n  sequence. (This was the numbering scheme prior to CodeIgniter 3.0.)\n* **Timestamp:** each migration is numbered using the timestamp when the migration\n  was created, in **YYYYMMDDHHIISS** format (e.g. **20121031100537**). This\n  helps prevent numbering conflicts when working in a team environment, and is\n  the preferred scheme in CodeIgniter 3.0 and later.\n\nThe desired style may be selected using the ``$config['migration_type']``\nsetting in your *application/config/migration.php* file.\n\nRegardless of which numbering style you choose to use, prefix your migration\nfiles with the migration number followed by an underscore and a descriptive\nname for the migration. For example:\n\n* 001_add_blog.php (sequential numbering)\n* 20121031100537_add_blog.php (timestamp numbering)\n\n******************\nCreate a Migration\n******************\n\t\nThis will be the first migration for a new site which has a blog. All \nmigrations go in the **application/migrations/** directory and have names such \nas *20121031100537_add_blog.php*.\n::\n\n\t<?php\n\n\tdefined('BASEPATH') OR exit('No direct script access allowed');\n\n\tclass Migration_Add_blog extends CI_Migration {\n\n\t\tpublic function up()\n\t\t{\n\t\t\t$this->dbforge->add_field(array(\n\t\t\t\t'blog_id' => array(\n\t\t\t\t\t'type' => 'INT',\n\t\t\t\t\t'constraint' => 5,\n\t\t\t\t\t'unsigned' => TRUE,\n\t\t\t\t\t'auto_increment' => TRUE\n\t\t\t\t),\n\t\t\t\t'blog_title' => array(\n\t\t\t\t\t'type' => 'VARCHAR',\n\t\t\t\t\t'constraint' => '100',\n\t\t\t\t),\n\t\t\t\t'blog_description' => array(\n\t\t\t\t\t'type' => 'TEXT',\n\t\t\t\t\t'null' => TRUE,\n\t\t\t\t),\n\t\t\t));\n\t\t\t$this->dbforge->add_key('blog_id', TRUE);\n\t\t\t$this->dbforge->create_table('blog');\n\t\t}\n\n\t\tpublic function down()\n\t\t{\n\t\t\t$this->dbforge->drop_table('blog');\n\t\t}\n\t}\n\nThen in **application/config/migration.php** set ``$config['migration_version'] = 20121031100537;``.\n\n*************\nUsage Example\n*************\n\nIn this example some simple code is placed in **application/controllers/Migrate.php** \nto update the schema.::\n\n\t<?php\n\t\n\tclass Migrate extends CI_Controller\n\t{\n\n\t\tpublic function index()\n\t\t{\n\t\t\t$this->load->library('migration');\n\n\t\t\tif ($this->migration->current() === FALSE)\n\t\t\t{\n\t\t\t\tshow_error($this->migration->error_string());\n\t\t\t}\n\t\t}\n\n\t}\n\n*********************\nMigration Preferences\n*********************\n\nThe following is a table of all the config options for migrations.\n\n========================== ====================== ========================== =============================================\nPreference                 Default                Options                    Description\n========================== ====================== ========================== =============================================\n**migration_enabled**      FALSE                  TRUE / FALSE               Enable or disable migrations.\n**migration_path**         APPPATH.'migrations/'  None                       The path to your migrations folder.\n**migration_version**      0                      None                       The current version your database should use.\n**migration_table**        migrations             None                       The table name for storing the schema\n                                                                             version number.\n**migration_auto_latest**  FALSE                  TRUE / FALSE               Enable or disable automatically \n                                                                             running migrations.\n**migration_type**         'timestamp'            'timestamp' / 'sequential' The type of numeric identifier used to name\n                                                                             migration files.\n========================== ====================== ========================== =============================================\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Migration\n\n\t.. php:method:: current()\n\n\t\t:returns:\tTRUE if no migrations are found, current version string on success, FALSE on failure\n\t\t:rtype:\tmixed\n\n\t\tMigrates up to the current version (whatever is set for\n\t\t``$config['migration_version']`` in *application/config/migration.php*).\n\n\t.. php:method:: error_string()\n\n\t\t:returns:\tError messages\n\t\t:rtype:\tstring\n\n\t\tThis returns a string of errors that were detected while performing a migration.\n\n\t.. php:method:: find_migrations()\n\n\t\t:returns:\tAn array of migration files\n\t\t:rtype:\tarray\n\n\t\tAn array of migration filenames are returned that are found in the **migration_path** property.\n\n\t.. php:method:: latest()\n\n\t\t:returns:\tCurrent version string on success, FALSE on failure\n\t\t:rtype:\tmixed\n\n\t\tThis works much the same way as ``current()`` but instead of looking for \n\t\tthe ``$config['migration_version']`` the Migration class will use the very \n\t\tnewest migration found in the filesystem.\n\n\t.. php:method:: version($target_version)\n\n\t\t:param\tmixed\t$target_version: Migration version to process\n\t\t:returns:\tTRUE if no migrations are found, current version string on success, FALSE on failure\n\t\t:rtype:\tmixed\n\n\t\tVersion can be used to roll back changes or step forwards programmatically to \n\t\tspecific versions. It works just like ``current()`` but ignores ``$config['migration_version']``.\n\t\t::\n\n\t\t\t$this->migration->version(5);\n"
  },
  {
    "path": "user_guide_src/source/libraries/output.rst",
    "content": "############\nOutput Class\n############\n\nThe Output class is a core class with one main function: To send the\nfinalized web page to the requesting browser. It is also responsible for\n:doc:`caching <../general/caching>` your web pages, if you use that\nfeature.\n\n.. note:: This class is initialized automatically by the system so there\n\tis no need to do it manually.\n\nUnder normal circumstances you won't even notice the Output class since\nit works transparently without your intervention. For example, when you\nuse the :doc:`Loader <../libraries/loader>` class to load a view file,\nit's automatically passed to the Output class, which will be called\nautomatically by CodeIgniter at the end of system execution. It is\npossible, however, for you to manually intervene with the output if you\nneed to.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Output\n\n\t.. attribute:: $parse_exec_vars = TRUE;\n\n\t\tEnables/disables parsing of the {elapsed_time} and {memory_usage} pseudo-variables.\n\n\t\tCodeIgniter will parse those tokens in your output by default. To disable this, set\n\t\tthis property to FALSE in your controller.\n\t\t::\n\n\t\t\t$this->output->parse_exec_vars = FALSE;\n\n\t.. php:method:: set_output($output)\n\n\t\t:param\tstring\t$output: String to set the output to\n\t\t:returns:\tCI_Output instance (method chaining)\n\t\t:rtype:\tCI_Output\n\n\t\tPermits you to manually set the final output string. Usage example::\n\n\t\t\t$this->output->set_output($data);\n\n\t\t.. important:: If you do set your output manually, it must be the last thing done\n\t\t\tin the function you call it from. For example, if you build a page in one\n\t\t\tof your controller methods, don't set the output until the end.\n\n\t.. php:method:: set_content_type($mime_type[, $charset = NULL])\n\n\t\t:param\tstring\t$mime_type: MIME Type idenitifer string\n\t\t:param\tstring\t$charset: Character set\n\t\t:returns:\tCI_Output instance (method chaining)\n\t\t:rtype:\tCI_Output\n\n\t\tPermits you to set the mime-type of your page so you can serve JSON data, JPEG's, XML, etc easily.\n\t\t::\n\n\t\t\t$this->output\n\t\t\t\t->set_content_type('application/json')\n\t\t\t\t->set_output(json_encode(array('foo' => 'bar')));\n\n\t\t\t$this->output\n\t\t\t\t->set_content_type('jpeg') // You could also use \".jpeg\" which will have the full stop removed before looking in config/mimes.php\n\t\t\t\t->set_output(file_get_contents('files/something.jpg'));\n\n\t\t.. important:: Make sure any non-mime string you pass to this method\n\t\t\texists in *application/config/mimes.php* or it will have no effect.\n\n\t\tYou can also set the character set of the document, by passing a second argument::\n\n\t\t\t$this->output->set_content_type('css', 'utf-8');\n\n\t.. php:method:: get_content_type()\n\n\t\t:returns:\tContent-Type string\n\t\t:rtype:\tstring\n\n\t\tReturns the Content-Type HTTP header that's currently in use, excluding the character set value.\n\t\t::\n\n\t\t\t$mime = $this->output->get_content_type();\n\n\t\t.. note:: If not set, the default return value is 'text/html'.\n\n\t.. php:method:: get_header($header)\n\n\t\t:param\tstring\t$header: HTTP header name\n\t\t:returns:\tHTTP response header or NULL if not found\n\t\t:rtype:\tmixed\n\n\t\tReturns the requested HTTP header value, or NULL if the requested header is not set.\n\t\tExample::\n\n\t\t\t$this->output->set_content_type('text/plain', 'UTF-8');\n\t\t\techo $this->output->get_header('content-type');\n\t\t\t// Outputs: text/plain; charset=utf-8\n\n\t\t.. note:: The header name is compared in a case-insensitive manner.\n\n\t\t.. note:: Raw headers sent via PHP's native ``header()`` function are also detected.\n\n\t.. php:method:: get_output()\n\n\t\t:returns:\tOutput string\n\t\t:rtype:\tstring\n\n\t\tPermits you to manually retrieve any output that has been sent for\n\t\tstorage in the output class. Usage example::\n\n\t\t\t$string = $this->output->get_output();\n\n\t\tNote that data will only be retrievable from this function if it has\n\t\tbeen previously sent to the output class by one of the CodeIgniter\n\t\tfunctions like ``$this->load->view()``.\n\n\t.. php:method:: append_output($output)\n\n\t\t:param\tstring\t$output: Additional output data to append\n\t\t:returns:\tCI_Output instance (method chaining)\n\t\t:rtype:\tCI_Output\n\n\t\tAppends data onto the output string.\n\t\t::\n\n\t\t\t$this->output->append_output($data);\n\n\t.. php:method:: set_header($header[, $replace = TRUE])\n\n\t\t:param\tstring\t$header: HTTP response header\n\t\t:param\tbool\t$replace: Whether to replace the old header value, if it is already set\n\t\t:returns:\tCI_Output instance (method chaining)\n\t\t:rtype:\tCI_Output\n\n\t\tPermits you to manually set server headers, which the output class will\n\t\tsend for you when outputting the final rendered display. Example::\n\n\t\t\t$this->output->set_header('HTTP/1.0 200 OK');\n\t\t\t$this->output->set_header('HTTP/1.1 200 OK');\n\t\t\t$this->output->set_header('Last-Modified: '.gmdate('D, d M Y H:i:s', $last_update).' GMT');\n\t\t\t$this->output->set_header('Cache-Control: no-store, no-cache, must-revalidate');\n\t\t\t$this->output->set_header('Cache-Control: post-check=0, pre-check=0');\n\t\t\t$this->output->set_header('Pragma: no-cache');\n\n\t.. php:method:: set_status_header([$code = 200[, $text = '']])\n\n\t\t:param\tint\t$code: HTTP status code\n\t\t:param\tstring\t$text: Optional message\n\t\t:returns:\tCI_Output instance (method chaining)\n\t\t:rtype:\tCI_Output\n\n\t\tPermits you to manually set a server status header. Example::\n\n\t\t\t$this->output->set_status_header(401);\n\t\t\t// Sets the header as:  Unauthorized\n\n\t\t`See here <https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html>`_ for a full list of headers.\n\n\t\t.. note:: This method is an alias for :doc:`Common function <../general/common_functions>`\n\t\t\t:func:`set_status_header()`.\n\n\t.. php:method:: enable_profiler([$val = TRUE])\n\n\t\t:param\tbool\t$val: Whether to enable or disable the Profiler\n\t\t:returns:\tCI_Output instance (method chaining)\n\t\t:rtype:\tCI_Output\n\n\t\tPermits you to enable/disable the :doc:`Profiler <../general/profiling>`, which will display benchmark\n\t\tand other data at the bottom of your pages for debugging and optimization purposes.\n\n\t\tTo enable the profiler place the following line anywhere within your\n\t\t:doc:`Controller <../general/controllers>` methods::\n\n\t\t\t$this->output->enable_profiler(TRUE);\n\n\t\tWhen enabled a report will be generated and inserted at the bottom of your pages.\n\n\t\tTo disable the profiler you would use::\n\n\t\t\t$this->output->enable_profiler(FALSE);\n\n\t.. php:method:: set_profiler_sections($sections)\n\n\t\t:param\tarray\t$sections: Profiler sections\n\t\t:returns:\tCI_Output instance (method chaining)\n\t\t:rtype:\tCI_Output\n\n\t\tPermits you to enable/disable specific sections of the Profiler when it is enabled.\n\t\tPlease refer to the :doc:`Profiler <../general/profiling>` documentation for further information.\n\n\t.. php:method:: cache($time)\n\n\t\t:param\tint\t$time: Cache expiration time in minutes\n\t\t:returns:\tCI_Output instance (method chaining)\n\t\t:rtype:\tCI_Output\n\n\t\tCaches the current page for the specified amount of minutes.\n\n\t\tFor more information, please see the :doc:`caching documentation <../general/caching>`.\n\n\t.. php:method:: _display([$output = NULL])\n\n\t\t:param\tstring\t$output: Output data override\n\t\t:returns:\tvoid\n\t\t:rtype:\tvoid\n\n\t\tSends finalized output data to the browser along with any server headers. It also stops benchmark\n\t\ttimers.\n\n\t\t.. note:: This method is called automatically at the end of script execution, you won't need to \n\t\t\tcall it manually unless you are aborting script execution using ``exit()`` or ``die()`` in your code.\n\t\t\n\t\tExample::\n\n\t\t\t$response = array('status' => 'OK');\n\n\t\t\t$this->output\n\t\t\t\t->set_status_header(200)\n\t\t\t\t->set_content_type('application/json', 'utf-8')\n\t\t\t\t->set_output(json_encode($response, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES))\n\t\t\t\t->_display();\n\t\t\texit;\n\n\t\t.. note:: Calling this method manually without aborting script execution will result in duplicated output.\n"
  },
  {
    "path": "user_guide_src/source/libraries/pagination.rst",
    "content": "################\nPagination Class\n################\n\nCodeIgniter's Pagination class is very easy to use, and it is 100%\ncustomizable, either dynamically or via stored preferences.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\nIf you are not familiar with the term \"pagination\", it refers to links\nthat allows you to navigate from page to page, like this::\n\n\t« First  < 1 2 3 4 5 >  Last »\n\n*******\nExample\n*******\n\nHere is a simple example showing how to create pagination in one of your\n:doc:`controller <../general/controllers>` methods::\n\n\t$this->load->library('pagination');\n\n\t$config['base_url'] = 'http://example.com/index.php/test/page/';\n\t$config['total_rows'] = 200;\n\t$config['per_page'] = 20;\n\n\t$this->pagination->initialize($config);\n\n\techo $this->pagination->create_links();\n\nNotes\n=====\n\nThe ``$config`` array contains your configuration variables. It is passed to\nthe ``$this->pagination->initialize()`` method as shown above. Although\nthere are some twenty items you can configure, at minimum you need the\nthree shown. Here is a description of what those items represent:\n\n-  **base_url** This is the full URL to the controller class/function\n   containing your pagination. In the example above, it is pointing to a\n   controller called \"Test\" and a function called \"page\". Keep in mind\n   that you can :doc:`re-route your URI <../general/routing>` if you\n   need a different structure.\n-  **total_rows** This number represents the total rows in the result\n   set you are creating pagination for. Typically this number will be\n   the total rows that your database query returned.\n-  **per_page** The number of items you intend to show per page. In the\n   above example, you would be showing 20 items per page.\n\nThe ``create_links()`` method returns an empty string when there is no\npagination to show.\n\nSetting preferences in a config file\n====================================\n\nIf you prefer not to set preferences using the above method, you can\ninstead put them into a config file. Simply create a new file called\npagination.php, add the ``$config`` array in that file. Then save the file\nin *application/config/pagination.php* and it will be used automatically.\nYou will NOT need to use ``$this->pagination->initialize()`` if you save\nyour preferences in a config file.\n\n**************************\nCustomizing the Pagination\n**************************\n\nThe following is a list of all the preferences you can pass to the\ninitialization function to tailor the display.\n\n**$config['uri_segment'] = 3;**\n\nThe pagination function automatically determines which segment of your\nURI contains the page number. If you need something different you can\nspecify it.\n\n**$config['num_links'] = 2;**\n\nThe number of \"digit\" links you would like before and after the selected\npage number. For example, the number 2 will place two digits on either\nside, as in the example links at the very top of this page.\n\n**$config['use_page_numbers'] = TRUE;**\n\nBy default, the URI segment will use the starting index for the items\nyou are paginating. If you prefer to show the the actual page number,\nset this to TRUE.\n\n**$config['page_query_string'] = TRUE;**\n\nBy default, the pagination library assume you are using :doc:`URI\nSegments <../general/urls>`, and constructs your links something\nlike::\n\n\thttp://example.com/index.php/test/page/20\n\nIf you have ``$config['enable_query_strings']`` set to TRUE your links\nwill automatically be re-written using Query Strings. This option can\nalso be explicitly set. Using ``$config['page_query_string']`` set to TRUE,\nthe pagination link will become::\n\n\thttp://example.com/index.php?c=test&m=page&per_page=20\n\nNote that \"per_page\" is the default query string passed, however can be\nconfigured using ``$config['query_string_segment'] = 'your_string'``\n\n**$config['reuse_query_string'] = FALSE;**\n\nBy default your Query String arguments (nothing to do with other\nquery string options) will be ignored. Setting this config to\nTRUE will add existing query string arguments back into the\nURL after the URI segment and before the suffix.::\n\n\thttp://example.com/index.php/test/page/20?query=search%term\n\nThis helps you mix together normal :doc:`URI Segments <../general/urls>`\nas well as query string arguments, which until 3.0 was not possible.\n\n**$config['prefix'] = '';**\n\nA custom prefix added to the path. The prefix value will be right before\nthe offset segment.\n\n**$config['suffix'] = '';**\n\nA custom suffix added to the path. The suffix value will be right after\nthe offset segment.\n\n**$config['use_global_url_suffix'] = FALSE;**\n\nWhen set to TRUE, it will **override** the ``$config['suffix']`` value and\ninstead set it to the one that you have in ``$config['url_suffix']`` in\nyour **application/config/config.php** file.\n\n***********************\nAdding Enclosing Markup\n***********************\n\nIf you would like to surround the entire pagination with some markup you\ncan do it with these two preferences:\n\n**$config['full_tag_open'] = '<p>';**\n\nThe opening tag placed on the left side of the entire result.\n\n**$config['full_tag_close'] = '</p>';**\n\nThe closing tag placed on the right side of the entire result.\n\n**************************\nCustomizing the First Link\n**************************\n\n**$config['first_link'] = 'First';**\n\nThe text you would like shown in the \"first\" link on the left. If you do\nnot want this link rendered, you can set its value to FALSE.\n\n.. note:: This value can also be translated via a language file.\n\n**$config['first_tag_open'] = '<div>';**\n\nThe opening tag for the \"first\" link.\n\n**$config['first_tag_close'] = '</div>';**\n\nThe closing tag for the \"first\" link.\n\n**$config['first_url'] = '';**\n\nAn alternative URL to use for the \"first page\" link.\n\n*************************\nCustomizing the Last Link\n*************************\n\n**$config['last_link'] = 'Last';**\n\nThe text you would like shown in the \"last\" link on the right. If you do\nnot want this link rendered, you can set its value to FALSE.\n\n.. note:: This value can also be translated via a language file.\n\n**$config['last_tag_open'] = '<div>';**\n\nThe opening tag for the \"last\" link.\n\n**$config['last_tag_close'] = '</div>';**\n\nThe closing tag for the \"last\" link.\n\n***************************\nCustomizing the \"Next\" Link\n***************************\n\n**$config['next_link'] = '&gt;';**\n\nThe text you would like shown in the \"next\" page link. If you do not\nwant this link rendered, you can set its value to FALSE.\n\n.. note:: This value can also be translated via a language file.\n\n**$config['next_tag_open'] = '<div>';**\n\nThe opening tag for the \"next\" link.\n\n**$config['next_tag_close'] = '</div>';**\n\nThe closing tag for the \"next\" link.\n\n*******************************\nCustomizing the \"Previous\" Link\n*******************************\n\n**$config['prev_link'] = '&lt;';**\n\nThe text you would like shown in the \"previous\" page link. If you do not\nwant this link rendered, you can set its value to FALSE.\n\n.. note:: This value can also be translated via a language file.\n\n**$config['prev_tag_open'] = '<div>';**\n\nThe opening tag for the \"previous\" link.\n\n**$config['prev_tag_close'] = '</div>';**\n\nThe closing tag for the \"previous\" link.\n\n***********************************\nCustomizing the \"Current Page\" Link\n***********************************\n\n**$config['cur_tag_open'] = '<b>';**\n\nThe opening tag for the \"current\" link.\n\n**$config['cur_tag_close'] = '</b>';**\n\nThe closing tag for the \"current\" link.\n\n****************************\nCustomizing the \"Digit\" Link\n****************************\n\n**$config['num_tag_open'] = '<div>';**\n\nThe opening tag for the \"digit\" link.\n\n**$config['num_tag_close'] = '</div>';**\n\nThe closing tag for the \"digit\" link.\n\n****************\nHiding the Pages\n****************\n\nIf you wanted to not list the specific pages (for example, you only want\n\"next\" and \"previous\" links), you can suppress their rendering by\nadding::\n\n\t $config['display_pages'] = FALSE;\n\n****************************\nAdding attributes to anchors\n****************************\n\nIf you want to add an extra attribute to be added to every link rendered\nby the pagination class, you can set them as key/value pairs in the\n\"attributes\" config::\n\n\t// Produces: class=\"myclass\"\n\t$config['attributes'] = array('class' => 'myclass');\n\n*****************************\nDisabling the \"rel\" attribute\n*****************************\n\nBy default the rel attribute is dynamically generated and appended to\nthe appropriate anchors. If for some reason you want to turn it off,\nyou can pass boolean FALSE as a regular attribute\n\n::\n\n\t$config['attributes']['rel'] = FALSE;\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Pagination\n\n\t.. php:method:: initialize([$params = array()])\n\n\t\t:param\tarray\t$params: Configuration parameters\n\t\t:returns:\tCI_Pagination instance (method chaining)\n\t\t:rtype:\tCI_Pagination\n\n\t\tInitializes the Pagination class with your preferred options.\n\n\t.. php:method:: create_links()\n\n\t\t:returns:\tHTML-formatted pagination\n\t\t:rtype:\tstring\n\n\t\tReturns a \"pagination\" bar, containing the generated links or an empty string if there's just a single page.\n"
  },
  {
    "path": "user_guide_src/source/libraries/parser.rst",
    "content": "#####################\nTemplate Parser Class\n#####################\n\nThe Template Parser Class can perform simple text substitution for \npseudo-variables contained within your view files. \nIt can parse simple variables or variable tag pairs. \n\nIf you've never used a template engine,\npseudo-variable names are enclosed in braces, like this::\n\n\t<html lang=\"en\">\n\t\t<head>\n\t\t\t<title>{blog_title}</title>\n\t\t</head>\n\t\t<body>\n\t\t\t<h3>{blog_heading}</h3>\n\n\t\t{blog_entries}\n\t\t\t<h5>{title}</h5>\n\t\t\t<p>{body}</p>\n\t\t{/blog_entries}\n\n\t\t</body>\n\t</html>\n\nThese variables are not actual PHP variables, but rather plain text\nrepresentations that allow you to eliminate PHP from your templates\n(view files).\n\n.. note:: CodeIgniter does **not** require you to use this class since\n\tusing pure PHP in your view pages lets them run a little faster.\n\tHowever, some developers prefer to use a template engine if\n        they work with designers who they feel would find some\n        confusion working with PHP.\n\n.. important:: The Template Parser Class is **not** a full-blown\n\ttemplate parsing solution. We've kept it very lean on purpose in order\n\tto maintain maximum performance.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n*******************************\nUsing the Template Parser Class\n*******************************\n\nInitializing the Class\n======================\n\nLike most other classes in CodeIgniter, the Parser class is initialized\nin your controller using the ``$this->load->library()`` method::\n\n\t$this->load->library('parser');\n\nOnce loaded, the Parser library object will be available using:\n$this->parser\n\nParsing templates\n=================\n\nYou can use the ``parse()`` method to parse (or render) simple templates,\nlike this::\n\n\t$data = array(\n\t\t'blog_title' => 'My Blog Title',\n\t\t'blog_heading' => 'My Blog Heading'\n\t);\n\n\t$this->parser->parse('blog_template', $data);\n\nThe first parameter contains the name of the :doc:`view\nfile <../general/views>` (in this example the file would be called\nblog_template.php), and the second parameter contains an associative\narray of data to be replaced in the template. In the above example, the\ntemplate would contain two variables: {blog_title} and {blog_heading}\n\nThere is no need to \"echo\" or do something with the data returned by\n$this->parser->parse(). It is automatically passed to the output class\nto be sent to the browser. However, if you do want the data returned\ninstead of sent to the output class you can pass TRUE (boolean) as the\nthird parameter::\n\n\t$string = $this->parser->parse('blog_template', $data, TRUE);\n\nVariable Pairs\n==============\n\nThe above example code allows simple variables to be replaced. What if\nyou would like an entire block of variables to be repeated, with each\niteration containing new values? Consider the template example we showed\nat the top of the page::\n\n\t<html lang=\"en\">\n\t\t<head>\n\t\t\t<title>{blog_title}</title>\n\t\t</head>\n\t\t<body>\n\t\t\t<h3>{blog_heading}</h3>\n\n\t\t{blog_entries}\n\t\t\t<h5>{title}</h5>\n\t\t\t<p>{body}</p>\n\t\t{/blog_entries}\n\n\t\t</body>\n\t</html>\n\nIn the above code you'll notice a pair of variables: {blog_entries}\ndata... {/blog_entries}. In a case like this, the entire chunk of data\nbetween these pairs would be repeated multiple times, corresponding to\nthe number of rows in the \"blog_entries\" element of the parameters array.\n\nParsing variable pairs is done using the identical code shown above to\nparse single variables, except, you will add a multi-dimensional array\ncorresponding to your variable pair data. Consider this example::\n\n\t$this->load->library('parser');\n\n\t$data = array(\n\t\t'blog_title'   => 'My Blog Title',\n\t\t'blog_heading' => 'My Blog Heading',\n\t\t'blog_entries' => array(\n\t\t\tarray('title' => 'Title 1', 'body' => 'Body 1'),\n\t\t\tarray('title' => 'Title 2', 'body' => 'Body 2'),\n\t\t\tarray('title' => 'Title 3', 'body' => 'Body 3'),\n\t\t\tarray('title' => 'Title 4', 'body' => 'Body 4'),\n\t\t\tarray('title' => 'Title 5', 'body' => 'Body 5')\n\t\t)\n\t);\n\n\t$this->parser->parse('blog_template', $data);\n\nIf your \"pair\" data is coming from a database result, which is already a\nmulti-dimensional array, you can simply use the database ``result_array()``\nmethod::\n\n\t$query = $this->db->query(\"SELECT * FROM blog\");\n\n\t$this->load->library('parser');\n\n\t$data = array(\n\t\t'blog_title'   => 'My Blog Title',\n\t\t'blog_heading' => 'My Blog Heading',\n\t\t'blog_entries' => $query->result_array()\n\t);\n\n\t$this->parser->parse('blog_template', $data);\n\nUsage Notes\n===========\n\nIf you include substitution parameters that are not referenced in your\ntemplate, they are ignored::\n\n\t$template = 'Hello, {firstname} {lastname}';\n\t$data = array(\n\t\t'title' => 'Mr',\n\t\t'firstname' => 'John',\n\t\t'lastname' => 'Doe'\n\t);\n\t$this->parser->parse_string($template, $data);\n\n\t// Result: Hello, John Doe\n\nIf you do not include a substitution parameter that is referenced in your\ntemplate, the original pseudo-variable is shown in the result::\n\n\t$template = 'Hello, {firstname} {initials} {lastname}';\n\t$data = array(\n\t\t'title' => 'Mr',\n\t\t'firstname' => 'John',\n\t\t'lastname' => 'Doe'\n\t);\n\t$this->parser->parse_string($template, $data);\n\n\t// Result: Hello, John {initials} Doe\n\nIf you provide a string substitution parameter when an array is expected,\ni.e. for a variable pair, the substitution is done for the opening variable\npair tag, but the closing variable pair tag is not rendered properly::\n\n\t$template = 'Hello, {firstname} {lastname} ({degrees}{degree} {/degrees})';\n\t$data = array(\n\t\t'degrees' => 'Mr',\n\t\t'firstname' => 'John',\n\t\t'lastname' => 'Doe',\n\t\t'titles' => array(\n\t\t\tarray('degree' => 'BSc'),\n\t\t\tarray('degree' => 'PhD')\n\t\t)\n\t);\n\t$this->parser->parse_string($template, $data);\n\n\t// Result: Hello, John Doe (Mr{degree} {/degrees})\n\nIf you name one of your individual substitution parameters the same as one\nused inside a variable pair, the results may not be as expected::\n\n\t$template = 'Hello, {firstname} {lastname} ({degrees}{degree} {/degrees})';\n\t$data = array(\n\t\t'degree' => 'Mr',\n\t\t'firstname' => 'John',\n\t\t'lastname' => 'Doe',\n\t\t'degrees' => array(\n\t\t\tarray('degree' => 'BSc'),\n\t\t\tarray('degree' => 'PhD')\n\t\t)\n\t);\n\t$this->parser->parse_string($template, $data);\n\n\t// Result: Hello, John Doe (Mr Mr )\n\nView Fragments\n==============\n\nYou do not have to use variable pairs to get the effect of iteration in\nyour views. It is possible to use a view fragment for what would be inside\na variable pair, and to control the iteration in your controller instead\nof in the view.\n\nAn example with the iteration controlled in the view::\n\n\t$template = '<ul>{menuitems}\n\t\t<li><a href=\"{link}\">{title}</a></li>\n\t{/menuitems}</ul>';\n\n\t$data = array(\n\t\t'menuitems' => array(\n\t\t\tarray('title' => 'First Link', 'link' => '/first'),\n\t\t\tarray('title' => 'Second Link', 'link' => '/second'),\n\t\t)\n\t);\n\t$this->parser->parse_string($template, $data);\n\nResult::\n\n\t<ul>\n\t\t<li><a href=\"/first\">First Link</a></li>\n\t\t<li><a href=\"/second\">Second Link</a></li>\n\t</ul>\n\nAn example with the iteration controlled in the controller, \nusing a view fragment::\n\n\t$temp = '';\n\t$template1 = '<li><a href=\"{link}\">{title}</a></li>';\n\t$data1 = array(\n\t\tarray('title' => 'First Link', 'link' => '/first'),\n\t\tarray('title' => 'Second Link', 'link' => '/second'),\n\t);\n\n\tforeach ($data1 as $menuitem)\n\t{\n\t\t$temp .= $this->parser->parse_string($template1, $menuitem, TRUE);\n\t}\n\n\t$template = '<ul>{menuitems}</ul>';\n\t$data = array(\n\t\t'menuitems' => $temp\n\t);\n\t$this->parser->parse_string($template, $data);\n\nResult::\n\n\t<ul>\n\t\t<li><a href=\"/first\">First Link</a></li>\n\t\t<li><a href=\"/second\">Second Link</a></li>\n\t</ul>\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Parser\n\n\t.. php:method:: parse($template, $data[, $return = FALSE])\n\n\t\t:param\tstring\t$template: Path to view file\n\t\t:param\tarray\t$data: Variable data\n\t\t:param\tbool\t$return: Whether to only return the parsed template\n\t\t:returns:\tParsed template string\n\t\t:rtype:\tstring\n\n\t\tParses a template from the provided path and variables.\n\n\t.. php:method:: parse_string($template, $data[, $return = FALSE])\n\n\t\t:param\tstring\t$template: Content to parse\n\t\t:param\tarray\t$data: Variable data\n\t\t:param\tbool\t$return: Whether to only return the parsed template\n\t\t:returns:\tParsed template string\n\t\t:rtype:\tstring\n\n\t\tThis method works exactly like ``parse()``, only it accepts\n\t\tthe template as a string instead of loading a view file.\n\n\t.. php:method:: set_delimiters([$l = '{'[, $r = '}']])\n\n\t\t:param\tstring\t$l: Left delimiter\n\t\t:param\tstring\t$r: Right delimiter\n\t\t:rtype: void\n\n\t\tSets the delimiters (opening and closing) for a\n\t\tpseudo-variable \"tag\" in a template.\n"
  },
  {
    "path": "user_guide_src/source/libraries/security.rst",
    "content": "##############\nSecurity Class\n##############\n\nThe Security Class contains methods that help you create a secure\napplication, processing input data for security.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n*************\nXSS Filtering\n*************\n\nCodeIgniter comes with a Cross Site Scripting prevention filter, which\nlooks for commonly used techniques to trigger JavaScript or other types\nof code that attempt to hijack cookies or do other malicious things.\nIf anything disallowed is encountered it is rendered safe by converting\nthe data to character entities.\n\nTo filter data through the XSS filter use the ``xss_clean()`` method::\n\n\t$data = $this->security->xss_clean($data);\n\nAn optional second parameter, *is_image*, allows this function to be used\nto test images for potential XSS attacks, useful for file upload\nsecurity. When this second parameter is set to TRUE, instead of\nreturning an altered string, the function returns TRUE if the image is\nsafe, and FALSE if it contained potentially malicious information that a\nbrowser may attempt to execute.\n\n::\n\n\tif ($this->security->xss_clean($file, TRUE) === FALSE)\n\t{\n\t\t// file failed the XSS test\n\t}\n\n.. important:: If you want to filter HTML attribute values, use\n\t:php:func:`html_escape()` instead!\n\n*********************************\nCross-site request forgery (CSRF)\n*********************************\n\nYou can enable CSRF protection by altering your **application/config/config.php**\nfile in the following way::\n\n\t$config['csrf_protection'] = TRUE;\n\nIf you use the :doc:`form helper <../helpers/form_helper>`, then\n:func:`form_open()` will automatically insert a hidden csrf field in\nyour forms. If not, then you can use ``get_csrf_token_name()``\nand ``get_csrf_hash()``\n::\n\n\t$csrf = array(\n\t\t'name' => $this->security->get_csrf_token_name(),\n\t\t'hash' => $this->security->get_csrf_hash()\n\t);\n\n\t...\n\n\t<input type=\"hidden\" name=\"<?=$csrf['name'];?>\" value=\"<?=$csrf['hash'];?>\" />\n\nTokens may be either regenerated on every submission (default) or\nkept the same throughout the life of the CSRF cookie. The default\nregeneration of tokens provides stricter security, but may result\nin usability concerns as other tokens become invalid (back/forward\nnavigation, multiple tabs/windows, asynchronous actions, etc). You\nmay alter this behavior by editing the following config parameter\n\n::\n\n\t$config['csrf_regenerate'] = TRUE;\n\nSelect URIs can be whitelisted from csrf protection (for example API\nendpoints expecting externally POSTed content). You can add these URIs\nby editing the 'csrf_exclude_uris' config parameter::\n\n\t$config['csrf_exclude_uris'] = array('api/person/add');\n\nRegular expressions are also supported (case-insensitive)::\n\n\t$config['csrf_exclude_uris'] = array(\n\t\t'api/record/[0-9]+',\n\t\t'api/title/[a-z]+'\n\t);\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Security\n\n\t.. php:method:: xss_clean($str[, $is_image = FALSE])\n\n\t\t:param\tmixed\t$str: Input string or an array of strings\n\t\t:returns:\tXSS-clean data\n\t\t:rtype:\tmixed\n\n\t\tTries to remove XSS exploits from the input data and returns the cleaned string.\n\t\tIf the optional second parameter is set to true, it will return boolean TRUE if\n\t\tthe image is safe to use and FALSE if malicious data was detected in it.\n\n\t\t.. important:: This method is not suitable for filtering HTML attribute values!\n\t\t\tUse :php:func:`html_escape()` for that instead.\n\n\t.. php:method:: sanitize_filename($str[, $relative_path = FALSE])\n\n\t\t:param\tstring\t$str: File name/path\n\t\t:param\tbool\t$relative_path: Whether to preserve any directories in the file path\n\t\t:returns:\tSanitized file name/path\n\t\t:rtype:\tstring\n\n\t\tTries to sanitize filenames in order to prevent directory traversal attempts\n\t\tand other security threats, which is particularly useful for files that were supplied via user input.\n\t\t::\n\n\t\t\t$filename = $this->security->sanitize_filename($this->input->post('filename'));\n\n\t\tIf it is acceptable for the user input to include relative paths, e.g.\n\t\t*file/in/some/approved/folder.txt*, you can set the second optional parameter, ``$relative_path`` to TRUE.\n\t\t::\n\n\t\t\t$filename = $this->security->sanitize_filename($this->input->post('filename'), TRUE);\n\n\t.. php:method:: get_csrf_token_name()\n\n\t\t:returns:\tCSRF token name\n\t\t:rtype:\tstring\n\n\t\tReturns the CSRF token name (the ``$config['csrf_token_name']`` value).\n\n\t.. php:method:: get_csrf_hash()\n\n\t\t:returns:\tCSRF hash\n\t\t:rtype:\tstring\n\n\t\tReturns the CSRF hash value. Useful in combination with ``get_csrf_token_name()``\n\t\tfor manually building forms or sending valid AJAX POST requests.\n\n\t.. php:method:: entity_decode($str[, $charset = NULL])\n\n\t\t:param\tstring\t$str: Input string\n\t\t:param\tstring\t$charset: Character set of the input string\n\t\t:returns:\tEntity-decoded string\n\t\t:rtype:\tstring\n\n\t\tThis method acts a lot like PHP's own native ``html_entity_decode()`` function in ENT_COMPAT mode, only\n\t\tit tries to detect HTML entities that don't end in a semicolon because some browsers allow that.\n\n\t\tIf the ``$charset`` parameter is left empty, then your configured ``$config['charset']`` value will be used.\n\n\t.. php:method:: get_random_bytes($length)\n\n\t\t:param\tint\t$length: Output length\n\t\t:returns:\tA binary stream of random bytes or FALSE on failure\n\t\t:rtype:\tstring\n\n\t\tA convenience method for getting proper random bytes via ``mcrypt_create_iv()``,\n\t\t``/dev/urandom`` or ``openssl_random_pseudo_bytes()`` (in that order), if one\n\t\tof them is available.\n\n\t\tUsed for generating CSRF and XSS tokens.\n\n\t\t.. note:: The output is NOT guaranteed to be cryptographically secure,\n\t\t\tjust the best attempt at that.\n"
  },
  {
    "path": "user_guide_src/source/libraries/sessions.rst",
    "content": "###############\nSession Library\n###############\n\nThe Session class permits you maintain a user's \"state\" and track their\nactivity while they browse your site.\n\nCodeIgniter comes with a few session storage drivers:\n\n  - files (default; file-system based)\n  - database\n  - redis\n  - memcached\n\nIn addition, you may create your own, custom session drivers based on other\nkinds of storage, while still taking advantage of the features of the\nSession class.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n***********************\nUsing the Session Class\n***********************\n\nInitializing a Session\n======================\n\nSessions will typically run globally with each page load, so the Session\nclass should either be initialized in your :doc:`controller\n<../general/controllers>` constructors, or it can be :doc:`auto-loaded\n<../general/autoloader>` by the system.\nFor the most part the session class will run unattended in the background,\nso simply initializing the class will cause it to read, create, and update\nsessions when necessary.\n\nTo initialize the Session class manually in your controller constructor,\nuse the ``$this->load->library()`` method::\n\n\t$this->load->library('session');\n\nOnce loaded, the Sessions library object will be available using::\n\n\t$this->session\n\n.. important:: Because the :doc:`Loader Class </libraries/loader>` is instantiated\n\tby CodeIgniter's base controller, make sure to call\n\t``parent::__construct()`` before trying to load a library from\n\tinside a controller constructor.\n\nHow do Sessions work?\n=====================\n\nWhen a page is loaded, the session class will check to see if valid\nsession cookie is sent by the user's browser. If a sessions cookie does\n**not** exist (or if it doesn't match one stored on the server or has\nexpired) a new session will be created and saved.\n\nIf a valid session does exist, its information will be updated. With each\nupdate, the session ID may be regenerated if configured to do so.\n\nIt's important for you to understand that once initialized, the Session\nclass runs automatically. There is nothing you need to do to cause the\nabove behavior to happen. You can, as you'll see below, work with session\ndata, but the process of reading, writing, and updating a session is\nautomatic.\n\n.. note:: Under CLI, the Session library will automatically halt itself,\n\tas this is a concept based entirely on the HTTP protocol.\n\nA note about concurrency\n------------------------\n\nUnless you're developing a website with heavy AJAX usage, you can skip this\nsection. If you are, however, and if you're experiencing performance\nissues, then this note is exactly what you're looking for.\n\nSessions in previous versions of CodeIgniter didn't implement locking,\nwhich meant that two HTTP requests using the same session could run exactly\nat the same time. To use a more appropriate technical term - requests were\nnon-blocking.\n\nHowever, non-blocking requests in the context of sessions also means\nunsafe, because modifications to session data (or session ID regeneration)\nin one request can interfere with the execution of a second, concurrent\nrequest. This detail was at the root of many issues and the main reason why\nCodeIgniter 3.0 has a completely re-written Session library.\n\nWhy are we telling you this? Because it is likely that after trying to\nfind the reason for your performance issues, you may conclude that locking\nis the issue and therefore look into how to remove the locks ...\n\nDO NOT DO THAT! Removing locks would be **wrong** and it will cause you\nmore problems!\n\nLocking is not the issue, it is a solution. Your issue is that you still\nhave the session open, while you've already processed it and therefore no\nlonger need it. So, what you need is to close the session for the\ncurrent request after you no longer need it.\n\nLong story short - call ``session_write_close()`` once you no longer need\nanything to do with session variables.\n\nWhat is Session Data?\n=====================\n\nSession data is simply an array associated with a particular session ID\n(cookie).\n\nIf you've used sessions in PHP before, you should be familiar with PHP's\n`$_SESSION superglobal <https://secure.php.net/manual/en/reserved.variables.session.php>`_\n(if not, please read the content on that link).\n\nCodeIgniter gives access to its session data through the same means, as it\nuses the session handlers' mechanism provided by PHP. Using session data is\nas simple as manipulating (read, set and unset values) the ``$_SESSION``\narray.\n\nIn addition, CodeIgniter also provides 2 special types of session data\nthat are further explained below: flashdata and tempdata.\n\n.. note:: In previous versions, regular session data in CodeIgniter was\n\treferred to as 'userdata'. Have this in mind if that term is used\n\telsewhere in the manual. Most of it is written to explain how\n\tthe custom 'userdata' methods work.\n\nRetrieving Session Data\n=======================\n\nAny piece of information from the session array is available through the\n``$_SESSION`` superglobal::\n\n\t$_SESSION['item']\n\nOr through the magic getter::\n\n\t$this->session->item\n\nAnd for backwards compatibility, through the ``userdata()`` method::\n\n\t$this->session->userdata('item');\n\nWhere item is the array key corresponding to the item you wish to fetch.\nFor example, to assign a previously stored 'name' item to the ``$name``\nvariable, you will do this::\n\n\t$name = $_SESSION['name'];\n\n\t// or:\n\n\t$name = $this->session->name\n\n\t// or:\n\n\t$name = $this->session->userdata('name');\n\n.. note:: The ``userdata()`` method returns NULL if the item you are trying\n\tto access does not exist.\n\nIf you want to retrieve all of the existing userdata, you can simply\nomit the item key (magic getter only works for properties)::\n\n\t$_SESSION\n\n\t// or:\n\n\t$this->session->userdata();\n\nAdding Session Data\n===================\n\nLet's say a particular user logs into your site. Once authenticated, you\ncould add their username and e-mail address to the session, making that\ndata globally available to you without having to run a database query when\nyou need it.\n\nYou can simply assign data to the ``$_SESSION`` array, as with any other\nvariable. Or as a property of ``$this->session``.\n\nAlternatively, the old method of assigning it as \"userdata\" is also\navailable. That however passing an array containing your new data to the\n``set_userdata()`` method::\n\n\t$this->session->set_userdata($array);\n\nWhere ``$array`` is an associative array containing your new data. Here's\nan example::\n\n\t$newdata = array(\n\t\t'username'  => 'johndoe',\n\t\t'email'     => 'johndoe@some-site.com',\n\t\t'logged_in' => TRUE\n\t);\n\n\t$this->session->set_userdata($newdata);\n\nIf you want to add userdata one value at a time, ``set_userdata()`` also\nsupports this syntax::\n\n\t$this->session->set_userdata('some_name', 'some_value');\n\nIf you want to verify that a session value exists, simply check with\n``isset()``::\n\n\t// returns FALSE if the 'some_name' item doesn't exist or is NULL,\n\t// TRUE otherwise:\n\tisset($_SESSION['some_name'])\n\nOr you can call ``has_userdata()``::\n\n\t$this->session->has_userdata('some_name');\n\nRemoving Session Data\n=====================\n\nJust as with any other variable, unsetting a value in ``$_SESSION`` can be\ndone through ``unset()``::\n\n\tunset($_SESSION['some_name']);\n\n\t// or multiple values:\n\n\tunset(\n\t\t$_SESSION['some_name'],\n\t\t$_SESSION['another_name']\n\t);\n\nAlso, just as ``set_userdata()`` can be used to add information to a\nsession, ``unset_userdata()`` can be used to remove it, by passing the\nsession key. For example, if you wanted to remove 'some_name' from your\nsession data array::\n\n\t$this->session->unset_userdata('some_name');\n\nThis method also accepts an array of item keys to unset::\n\n\t$array_items = array('username', 'email');\n\n\t$this->session->unset_userdata($array_items);\n\n.. note:: In previous versions, the ``unset_userdata()`` method used\n\tto accept an associative array of ``key => 'dummy value'``\n\tpairs. This is no longer supported.\n\nFlashdata\n=========\n\nCodeIgniter supports \"flashdata\", or session data that will only be\navailable for the next request, and is then automatically cleared.\n\nThis can be very useful, especially for one-time informational, error or\nstatus messages (for example: \"Record 2 deleted\").\n\nIt should be noted that flashdata variables are regular session vars,\nonly marked in a specific way under the '__ci_vars' key (please don't touch\nthat one, you've been warned).\n\nTo mark an existing item as \"flashdata\"::\n\n\t$this->session->mark_as_flash('item');\n\nIf you want to mark multiple items as flashdata, simply pass the keys as an\narray::\n\n\t$this->session->mark_as_flash(array('item', 'item2'));\n\nTo add flashdata::\n\n\t$_SESSION['item'] = 'value';\n\t$this->session->mark_as_flash('item');\n\nOr alternatively, using the ``set_flashdata()`` method::\n\n\t$this->session->set_flashdata('item', 'value');\n\nYou can also pass an array to ``set_flashdata()``, in the same manner as\n``set_userdata()``.\n\nReading flashdata variables is the same as reading regular session data\nthrough ``$_SESSION``::\n\n\t$_SESSION['item']\n\n.. important:: The ``userdata()`` method will NOT return flashdata items.\n\nHowever, if you want to be sure that you're reading \"flashdata\" (and not\nany other kind), you can also use the ``flashdata()`` method::\n\n\t$this->session->flashdata('item');\n\nOr to get an array with all flashdata, simply omit the key parameter::\n\n\t$this->session->flashdata();\n\n.. note:: The ``flashdata()`` method returns NULL if the item cannot be\n\tfound.\n\nIf you find that you need to preserve a flashdata variable through an\nadditional request, you can do so using the ``keep_flashdata()`` method.\nYou can either pass a single item or an array of flashdata items to keep.\n\n::\n\n\t$this->session->keep_flashdata('item');\n\t$this->session->keep_flashdata(array('item1', 'item2', 'item3'));\n\nTempdata\n========\n\nCodeIgniter also supports \"tempdata\", or session data with a specific\nexpiration time. After the value expires, or the session expires or is\ndeleted, the value is automatically removed.\n\nSimilarly to flashdata, tempdata variables are regular session vars that\nare marked in a specific way under the '__ci_vars' key (again, don't touch\nthat one).\n\nTo mark an existing item as \"tempdata\", simply pass its key and expiry time\n(in seconds!) to the ``mark_as_temp()`` method::\n\n\t// 'item' will be erased after 300 seconds\n\t$this->session->mark_as_temp('item', 300);\n\nYou can mark multiple items as tempdata in two ways, depending on whether\nyou want them all to have the same expiry time or not::\n\n\t// Both 'item' and 'item2' will expire after 300 seconds\n\t$this->session->mark_as_temp(array('item', 'item2'), 300);\n\n\t// 'item' will be erased after 300 seconds, while 'item2'\n\t// will do so after only 240 seconds\n\t$this->session->mark_as_temp(array(\n\t\t'item'\t=> 300,\n\t\t'item2'\t=> 240\n\t));\n\nTo add tempdata::\n\n\t$_SESSION['item'] = 'value';\n\t$this->session->mark_as_temp('item', 300); // Expire in 5 minutes\n\nOr alternatively, using the ``set_tempdata()`` method::\n\n\t$this->session->set_tempdata('item', 'value', 300);\n\nYou can also pass an array to ``set_tempdata()``::\n\n\t$tempdata = array('newuser' => TRUE, 'message' => 'Thanks for joining!');\n\n\t$this->session->set_tempdata($tempdata, NULL, $expire);\n\n.. note:: If the expiration is omitted or set to 0, the default\n\ttime-to-live value of 300 seconds (or 5 minutes) will be used.\n\nTo read a tempdata variable, again you can just access it through the\n``$_SESSION`` superglobal array::\n\n\t$_SESSION['item']\n\n.. important:: The ``userdata()`` method will NOT return tempdata items.\n\nOr if you want to be sure that you're reading \"tempdata\" (and not any\nother kind), you can also use the ``tempdata()`` method::\n\n\t$this->session->tempdata('item');\n\nAnd of course, if you want to retrieve all existing tempdata::\n\n\t$this->session->tempdata();\n\n.. note:: The ``tempdata()`` method returns NULL if the item cannot be\n\tfound.\n\nIf you need to remove a tempdata value before it expires, you can directly\nunset it from the ``$_SESSION`` array::\n\n\tunset($_SESSION['item']);\n\nHowever, this won't remove the marker that makes this specific item to be\ntempdata (it will be invalidated on the next HTTP request), so if you\nintend to reuse that same key in the same request, you'd want to use\n``unset_tempdata()``::\n\n\t$this->session->unset_tempdata('item');\n\nDestroying a Session\n====================\n\nTo clear the current session (for example, during a logout), you may\nsimply use either PHP's `session_destroy() <https://secure.php.net/session_destroy>`_\nfunction, or the ``sess_destroy()`` method. Both will work in exactly the\nsame way::\n\n\tsession_destroy();\n\n\t// or\n\n\t$this->session->sess_destroy();\n\n.. note:: This must be the last session-related operation that you do\n\tduring the same request. All session data (including flashdata and\n\ttempdata) will be destroyed permanently and functions will be\n\tunusable during the same request after you destroy the session.\n\nAccessing session metadata\n==========================\n\nIn previous CodeIgniter versions, the session data array included 4 items\nby default: 'session_id', 'ip_address', 'user_agent', 'last_activity'.\n\nThis was due to the specifics of how sessions worked, but is now no longer\nnecessary with our new implementation. However, it may happen that your\napplication relied on these values, so here are alternative methods of\naccessing them:\n\n  - session_id: ``session_id()``\n  - ip_address: ``$_SERVER['REMOTE_ADDR']``\n  - user_agent: ``$this->input->user_agent()`` (unused by sessions)\n  - last_activity: Depends on the storage, no straightforward way. Sorry!\n\nSession Preferences\n===================\n\nCodeIgniter will usually make everything work out of the box. However,\nSessions are a very sensitive component of any application, so some\ncareful configuration must be done. Please take your time to consider\nall of the options and their effects.\n\nYou'll find the following Session related preferences in your\n**application/config/config.php** file:\n\n============================ =============== ======================================== ============================================================================================\nPreference                   Default         Options                                  Description\n============================ =============== ======================================== ============================================================================================\n**sess_driver**              files           files/database/redis/memcached/*custom*  The session storage driver to use.\n**sess_cookie_name**         ci_session      [A-Za-z\\_-] characters only              The name used for the session cookie.\n**sess_samesite**            ci_session      'Lax', 'Strict' or 'None'                SameSite attribute value for session cookies.\n                                                                                      Defaults to ``session.cookie_samesite`` on PHP 7.3+ or 'Lax' if not present at all.\n**sess_expiration**          7200 (2 hours)  Time in seconds (integer)                The number of seconds you would like the session to last.\n                                                                                      If you would like a non-expiring session (until browser is closed) set the value to zero: 0\n**sess_save_path**           NULL            None                                     Specifies the storage location, depends on the driver being used.\n**sess_match_ip**            FALSE           TRUE/FALSE (boolean)                     Whether to validate the user's IP address when reading the session cookie.\n                                                                                      Note that some ISPs dynamically changes the IP, so if you want a non-expiring session you\n                                                                                      will likely set this to FALSE.\n**sess_time_to_update**      300             Time in seconds (integer)                This option controls how often the session class will regenerate itself and create a new\n                                                                                      session ID. Setting it to 0 will disable session ID regeneration.\n**sess_regenerate_destroy**  FALSE           TRUE/FALSE (boolean)                     Whether to destroy session data associated with the old session ID when auto-regenerating\n                                                                                      the session ID. When set to FALSE, the data will be later deleted by the garbage collector.\n============================ =============== ======================================== ============================================================================================\n\n.. note:: As a last resort, the Session library will try to fetch PHP's\n\tsession related INI settings, as well as legacy CI settings such as\n\t'sess_expire_on_close' when any of the above is not configured.\n\tHowever, you should never rely on this behavior as it can cause\n\tunexpected results or be changed in the future. Please configure\n\teverything properly.\n\nIn addition to the values above, the cookie and native drivers apply the\nfollowing configuration values shared by the :doc:`Input <input>` and\n:doc:`Security <security>` classes:\n\n================== =============== ===========================================================================\nPreference         Default         Description\n================== =============== ===========================================================================\n**cookie_domain**  ''              The domain for which the session is applicable\n**cookie_path**    /               The path to which the session is applicable\n**cookie_secure**  FALSE           Whether to create the session cookie only on encrypted (HTTPS) connections\n================== =============== ===========================================================================\n\n.. note:: The 'cookie_httponly' setting doesn't have an effect on sessions.\n\tInstead the HttpOnly parameter is always enabled, for security\n\treasons. Additionally, the 'cookie_prefix' setting is completely\n\tignored.\n\nSession Drivers\n===============\n\nAs already mentioned, the Session library comes with 4 drivers, or storage\nengines, that you can use:\n\n  - files\n  - database\n  - redis\n  - memcached\n\nBy default, the `Files Driver`_ will be used when a session is initialized,\nbecause it is the most safe choice and is expected to work everywhere\n(virtually every environment has a file system).\n\nHowever, any other driver may be selected via the ``$config['sess_driver']``\nline in your **application/config/config.php** file, if you chose to do so.\nHave it in mind though, every driver has different caveats, so be sure to\nget yourself familiar with them (below) before you make that choice.\n\nIn addition, you may also create and use `Custom Drivers`_, if the ones\nprovided by default don't satisfy your use case.\n\n.. note:: In previous CodeIgniter versions, a different, \"cookie driver\"\n\twas the only option and we have received negative feedback on not\n\tproviding that option. While we do listen to feedback from the\n\tcommunity, we want to warn you that it was dropped because it is\n\t**unsafe** and we advise you NOT to try to replicate it via a\n\tcustom driver.\n\nFiles Driver\n------------\n\nThe 'files' driver uses your file system for storing session data.\n\nIt can safely be said that it works exactly like PHP's own default session\nimplementation, but in case this is an important detail for you, have it\nmind that it is in fact not the same code and it has some limitations\n(and advantages).\n\nTo be more specific, it doesn't support PHP's `directory level and mode\nformats used in session.save_path\n<https://secure.php.net/manual/en/session.configuration.php#ini.session.save-path>`_,\nand it has most of the options hard-coded for safety. Instead, only\nabsolute paths are supported for ``$config['sess_save_path']``.\n\nAnother important thing that you should know, is to make sure that you\ndon't use a publicly-readable or shared directory for storing your session\nfiles. Make sure that *only you* have access to see the contents of your\nchosen *sess_save_path* directory. Otherwise, anybody who can do that, can\nalso steal any of the current sessions (also known as \"session fixation\"\nattack).\n\nOn UNIX-like operating systems, this is usually achieved by setting the\n0700 mode permissions on that directory via the `chmod` command, which\nallows only the directory's owner to perform read and write operations on\nit. But be careful because the system user *running* the script is usually\nnot your own, but something like 'www-data' instead, so only setting those\npermissions will probable break your application.\n\nInstead, you should do something like this, depending on your environment\n::\n\n\tmkdir /<path to your application directory>/sessions/\n\tchmod 0700 /<path to your application directory>/sessions/\n\tchown www-data /<path to your application directory>/sessions/\n\nBonus Tip\n^^^^^^^^^\n\nSome of you will probably opt to choose another session driver because\nfile storage is usually slower. This is only half true.\n\nA very basic test will probably trick you into believing that an SQL\ndatabase is faster, but in 99% of the cases, this is only true while you\nonly have a few current sessions. As the sessions count and server loads\nincrease - which is the time when it matters - the file system will\nconsistently outperform almost all relational database setups.\n\nIn addition, if performance is your only concern, you may want to look\ninto using `tmpfs <https://eddmann.com/posts/storing-php-sessions-file-caches-in-memory-using-tmpfs/>`_,\n(warning: external resource), which can make your sessions blazing fast.\n\nDatabase Driver\n---------------\n\nThe 'database' driver uses a relational database such as MySQL or\nPostgreSQL to store sessions. This is a popular choice among many users,\nbecause it allows the developer easy access to the session data within\nan application - it is just another table in your database.\n\nHowever, there are some conditions that must be met:\n\n  - Only your **default** database connection (or the one that you access\n    as ``$this->db`` from your controllers) can be used.\n  - You can NOT use a persistent connection.\n  - You can NOT use a connection with the *cache_on* setting enabled.\n\nIn order to use the 'database' session driver, you must also create this\ntable that we already mentioned and then set it as your\n``$config['sess_save_path']`` value.\nFor example, if you would like to use 'ci_sessions' as your table name,\nyou would do this::\n\n\t$config['sess_driver'] = 'database';\n\t$config['sess_save_path'] = 'ci_sessions';\n\n.. note:: If you've upgraded from a previous version of CodeIgniter and\n\tyou don't have 'sess_save_path' configured, then the Session\n\tlibrary will look for the old 'sess_table_name' setting and use\n\tit instead. Please don't rely on this behavior as it will get\n\tremoved in the future.\n\nAnd then of course, create the database table ...\n\nFor MySQL::\n\n\tCREATE TABLE IF NOT EXISTS `ci_sessions` (\n\t\t`id` varchar(128) NOT NULL,\n\t\t`ip_address` varchar(45) NOT NULL,\n\t\t`timestamp` int(10) unsigned DEFAULT 0 NOT NULL,\n\t\t`data` blob NOT NULL,\n\t\tKEY `ci_sessions_timestamp` (`timestamp`)\n\t);\n\nFor PostgreSQL::\n\n\tCREATE TABLE \"ci_sessions\" (\n\t\t\"id\" varchar(128) NOT NULL,\n\t\t\"ip_address\" varchar(45) NOT NULL,\n\t\t\"timestamp\" bigint DEFAULT 0 NOT NULL,\n\t\t\"data\" text DEFAULT '' NOT NULL\n\t);\n\n\tCREATE INDEX \"ci_sessions_timestamp\" ON \"ci_sessions\" (\"timestamp\");\n\nYou will also need to add a PRIMARY KEY **depending on your 'sess_match_ip'\nsetting**. The examples below work both on MySQL and PostgreSQL::\n\n\t// When sess_match_ip = TRUE\n\tALTER TABLE ci_sessions ADD PRIMARY KEY (id, ip_address);\n\n\t// When sess_match_ip = FALSE\n\tALTER TABLE ci_sessions ADD PRIMARY KEY (id);\n\n\t// To drop a previously created primary key (use when changing the setting)\n\tALTER TABLE ci_sessions DROP PRIMARY KEY;\n\n\n.. important:: Only MySQL and PostgreSQL databases are officially\n\tsupported, due to lack of advisory locking mechanisms on other\n\tplatforms. Using sessions without locks can cause all sorts of\n\tproblems, especially with heavy usage of AJAX, and we will not\n\tsupport such cases. Use ``session_write_close()`` after you've\n\tdone processing session data if you're having performance\n\tissues.\n\nRedis Driver\n------------\n\n.. note:: Since Redis doesn't have a locking mechanism exposed, locks for\n\tthis driver are emulated by a separate value that is kept for up\n\tto 300 seconds.\n\nRedis is a storage engine typically used for caching and popular because\nof its high performance, which is also probably your reason to use the\n'redis' session driver.\n\nThe downside is that it is not as ubiquitous as relational databases and\nrequires the `phpredis <https://github.com/phpredis/phpredis>`_ PHP\nextension to be installed on your system, and that one doesn't come\nbundled with PHP.\nChances are, you're only be using the 'redis' driver only if you're already\nboth familiar with Redis and using it for other purposes.\n\nJust as with the 'files' and 'database' drivers, you must also configure\nthe storage location for your sessions via the\n``$config['sess_save_path']`` setting.\nThe format here is a bit different and complicated at the same time. It is\nbest explained by the *phpredis* extension's README file, so we'll simply\nlink you to it:\n\n\thttps://github.com/phpredis/phpredis#php-session-handler\n\n.. warning:: CodeIgniter's Session library does NOT use the actual 'redis'\n\t``session.save_handler``. Take note **only** of the path format in\n\tthe link above.\n\nFor the most common case however, a simple ``host:port`` pair should be\nsufficient::\n\n\t$config['sess_driver'] = 'redis';\n\t$config['sess_save_path'] = 'tcp://localhost:6379';\n\nMemcached Driver\n----------------\n\n.. note:: Since Memcache doesn't have a locking mechanism exposed, locks\n\tfor this driver are emulated by a separate value that is kept for\n\tup to 300 seconds.\n\nThe 'memcached' driver is very similar to the 'redis' one in all of its\nproperties, except perhaps for availability, because PHP's `Memcached\n<https://secure.php.net/memcached>`_ extension is distributed via PECL and some\nLinux distrubutions make it available as an easy to install package.\n\nOther than that, and without any intentional bias towards Redis, there's\nnot much different to be said about Memcached - it is also a popular\nproduct that is usually used for caching and famed for its speed.\n\nHowever, it is worth noting that the only guarantee given by Memcached\nis that setting value X to expire after Y seconds will result in it being\ndeleted after Y seconds have passed (but not necessarily that it won't\nexpire earlier than that time). This happens very rarely, but should be\nconsidered as it may result in loss of sessions.\n\nThe ``$config['sess_save_path']`` format is fairly straightforward here,\nbeing just a ``host:port`` pair::\n\n\t$config['sess_driver'] = 'memcached';\n\t$config['sess_save_path'] = 'localhost:11211';\n\nBonus Tip\n^^^^^^^^^\n\nMulti-server configuration with an optional *weight* parameter as the\nthird colon-separated (``:weight``) value is also supported, but we have\nto note that we haven't tested if that is reliable.\n\nIf you want to experiment with this feature (on your own risk), simply\nseparate the multiple server paths with commas::\n\n\t// localhost will be given higher priority (5) here,\n\t// compared to 192.0.2.1 with a weight of 1.\n\t$config['sess_save_path'] = 'localhost:11211:5,192.0.2.1:11211:1';\n\nCustom Drivers\n--------------\n\nYou may also create your own, custom session drivers. However, have it in\nmind that this is typically not an easy task, as it takes a lot of\nknowledge to do it properly.\n\nYou need to know not only how sessions work in general, but also how they\nwork specifically in PHP, how the underlying storage mechanism works, how\nto handle concurrency, avoid deadlocks (but NOT through lack of locks) and\nlast but not least - how to handle the potential security issues, which\nis far from trivial.\n\nLong story short - if you don't know how to do that already in raw PHP,\nyou shouldn't be trying to do it within CodeIgniter either. You've been\nwarned.\n\nIf you only want to add some extra functionality to your sessions, just\nextend the base Session class, which is a lot more easier. Read the\n:doc:`Creating Libraries <../general/creating_libraries>` article to\nlearn how to do that.\n\nNow, to the point - there are three general rules that you must follow\nwhen creating a session driver for CodeIgniter:\n\n  - Put your driver's file under **application/libraries/Session/drivers/**\n    and follow the naming conventions used by the Session class.\n\n    For example, if you were to create a 'dummy' driver, you would have\n    a ``Session_dummy_driver`` class name, that is declared in\n    *application/libraries/Session/drivers/Session_dummy_driver.php*.\n\n  - Extend the ``CI_Session_driver`` class.\n\n    This is just a basic class with a few internal helper methods. It is\n    also extendable like any other library, if you really need to do that,\n    but we are not going to explain how ... if you're familiar with how\n    class extensions/overrides work in CI, then you already know how to do\n    it. If not, well, you shouldn't be doing it in the first place.\n\n\n  - Implement the `SessionHandlerInterface\n    <https://secure.php.net/sessionhandlerinterface>`_ interface.\n\n    .. note:: You may notice that ``SessionHandlerInterface`` is provided\n        by PHP since version 5.4.0. CodeIgniter will automatically declare\n        the same interface if you're running an older PHP version.\n\n    The link will explain why and how.\n\nSo, based on our 'dummy' driver example above, you'd end up with something\nlike this::\n\n\t// application/libraries/Session/drivers/Session_dummy_driver.php:\n\n\tclass CI_Session_dummy_driver extends CI_Session_driver implements SessionHandlerInterface\n\t{\n\n\t\tpublic function __construct(&$params)\n\t\t{\n\t\t\t// DO NOT forget this\n\t\t\tparent::__construct($params);\n\n\t\t\t// Configuration & other initializations\n\t\t}\n\n\t\tpublic function open($save_path, $name)\n\t\t{\n\t\t\t// Initialize storage mechanism (connection)\n\t\t}\n\n\t\tpublic function read($session_id)\n\t\t{\n\t\t\t// Read session data (if exists), acquire locks\n\t\t}\n\n\t\tpublic function write($session_id, $session_data)\n\t\t{\n\t\t\t// Create / update session data (it might not exist!)\n\t\t}\n\n\t\tpublic function close()\n\t\t{\n\t\t\t// Free locks, close connections / streams / etc.\n\t\t}\n\n\t\tpublic function destroy($session_id)\n\t\t{\n\t\t\t// Call close() method & destroy data for current session (order may differ)\n\t\t}\n\n\t\tpublic function gc($maxlifetime)\n\t\t{\n\t\t\t// Erase data for expired sessions\n\t\t}\n\n\t}\n\nIf you've done everything properly, you can now set your *sess_driver*\nconfiguration value to 'dummy' and use your own driver. Congratulations!\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Session\n\n\t.. php:method:: userdata([$key = NULL])\n\n\t\t:param\tmixed\t$key: Session item key or NULL\n\t\t:returns:\tValue of the specified item key, or an array of all userdata\n\t\t:rtype:\tmixed\n\n\t\tGets the value for a specific ``$_SESSION`` item, or an\n\t\tarray of all \"userdata\" items if not key was specified.\n\t\n\t\t.. note:: This is a legacy method kept only for backwards\n\t\t\tcompatibility with older applications. You should\n\t\t\tdirectly access ``$_SESSION`` instead.\n\n\t.. php:method:: all_userdata()\n\n\t\t:returns:\tAn array of all userdata\n\t\t:rtype:\tarray\n\n\t\tReturns an array containing all \"userdata\" items.\n\n\t\t.. note:: This method is DEPRECATED. Use ``userdata()``\n\t\t\twith no parameters instead.\n\n\t.. php:method:: &get_userdata()\n\n\t\t:returns:\tA reference to ``$_SESSION``\n\t\t:rtype:\tarray\n\n\t\tReturns a reference to the ``$_SESSION`` array.\n\n\t\t.. note:: This is a legacy method kept only for backwards\n\t\t\tcompatibility with older applications.\n\n\t.. php:method:: has_userdata($key)\n\n\t\t:param\tstring\t$key: Session item key\n\t\t:returns:\tTRUE if the specified key exists, FALSE if not\n\t\t:rtype:\tbool\n\n\t\tChecks if an item exists in ``$_SESSION``.\n\n\t\t.. note:: This is a legacy method kept only for backwards\n\t\t\tcompatibility with older applications. It is just\n\t\t\tan alias for ``isset($_SESSION[$key])`` - please\n\t\t\tuse that instead.\n\n\t.. php:method:: set_userdata($data[, $value = NULL])\n\n\t\t:param\tmixed\t$data: An array of key/value pairs to set as session data, or the key for a single item\n\t\t:param\tmixed\t$value:\tThe value to set for a specific session item, if $data is a key\n\t\t:rtype:\tvoid\n\n\t\tAssigns data to the ``$_SESSION`` superglobal.\n\n\t\t.. note:: This is a legacy method kept only for backwards\n\t\t\tcompatibility with older applications.\n\n\t.. php:method:: unset_userdata($key)\n\n\t\t:param\tmixed\t$key: Key for the session data item to unset, or an array of multiple keys\n\t\t:rtype:\tvoid\n\n\t\tUnsets the specified key(s) from the ``$_SESSION``\n\t\tsuperglobal.\n\n\t\t.. note:: This is a legacy method kept only for backwards\n\t\t\tcompatibility with older applications. It is just\n\t\t\tan alias for ``unset($_SESSION[$key])`` - please\n\t\t\tuse that instead.\n\n\t.. php:method:: mark_as_flash($key)\n\n\t\t:param\tmixed\t$key: Key to mark as flashdata, or an array of multiple keys\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tMarks a ``$_SESSION`` item key (or multiple ones) as\n\t\t\"flashdata\".\n\n\t.. php:method:: get_flash_keys()\n\n\t\t:returns:\tArray containing the keys of all \"flashdata\" items.\n\t\t:rtype:\tarray\n\n\t\tGets a list of all ``$_SESSION`` that have been marked as\n\t\t\"flashdata\".\n\n\t.. php:method:: unmark_flash($key)\n\n\t\t:param\tmixed\t$key: Key to be un-marked as flashdata, or an array of multiple keys\n\t\t:rtype:\tvoid\n\n\t\tUnmarks a ``$_SESSION`` item key (or multiple ones) as\n\t\t\"flashdata\".\n\n\t.. php:method:: flashdata([$key = NULL])\n\n\t\t:param\tmixed\t$key: Flashdata item key or NULL\n\t\t:returns:\tValue of the specified item key, or an array of all flashdata\n\t\t:rtype:\tmixed\n\n\t\tGets the value for a specific ``$_SESSION`` item that has\n\t\tbeen marked as \"flashdata\", or an array of all \"flashdata\"\n\t\titems if no key was specified.\n\t\n\t\t.. note:: This is a legacy method kept only for backwards\n\t\t\tcompatibility with older applications. You should\n\t\t\tdirectly access ``$_SESSION`` instead.\n\n\t.. php:method:: keep_flashdata($key)\n\n\t\t:param\tmixed\t$key: Flashdata key to keep, or an array of multiple keys\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tRetains the specified session data key(s) as \"flashdata\"\n\t\tthrough the next request.\n\n\t\t.. note:: This is a legacy method kept only for backwards\n\t\t\tcompatibility with older applications. It is just\n\t\t\tan alias for the ``mark_as_flash()`` method.\n\n\t.. php:method:: set_flashdata($data[, $value = NULL])\n\n\t\t:param\tmixed\t$data: An array of key/value pairs to set as flashdata, or the key for a single item\n\t\t:param\tmixed\t$value:\tThe value to set for a specific session item, if $data is a key\n\t\t:rtype:\tvoid\n\n\t\tAssigns data to the ``$_SESSION`` superglobal and marks it\n\t\tas \"flashdata\".\n\n\t\t.. note:: This is a legacy method kept only for backwards\n\t\t\tcompatibility with older applications.\n\n\t.. php:method:: mark_as_temp($key[, $ttl = 300])\n\n\t\t:param\tmixed\t$key: Key to mark as tempdata, or an array of multiple keys\n\t\t:param\tint\t$ttl: Time-to-live value for the tempdata, in seconds\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tMarks a ``$_SESSION`` item key (or multiple ones) as\n\t\t\"tempdata\".\n\n\t.. php:method:: get_temp_keys()\n\n\t\t:returns:\tArray containing the keys of all \"tempdata\" items.\n\t\t:rtype:\tarray\n\n\t\tGets a list of all ``$_SESSION`` that have been marked as\n\t\t\"tempdata\".\n\n\t.. php:method:: unmark_temp($key)\n\n\t\t:param\tmixed\t$key: Key to be un-marked as tempdata, or an array of multiple keys\n\t\t:rtype:\tvoid\n\n\t\tUnmarks a ``$_SESSION`` item key (or multiple ones) as\n\t\t\"tempdata\".\n\n\t.. php:method:: tempdata([$key = NULL])\n\n\t\t:param\tmixed\t$key: Tempdata item key or NULL\n\t\t:returns:\tValue of the specified item key, or an array of all tempdata\n\t\t:rtype:\tmixed\n\n\t\tGets the value for a specific ``$_SESSION`` item that has\n\t\tbeen marked as \"tempdata\", or an array of all \"tempdata\"\n\t\titems if no key was specified.\n\t\n\t\t.. note:: This is a legacy method kept only for backwards\n\t\t\tcompatibility with older applications. You should\n\t\t\tdirectly access ``$_SESSION`` instead.\n\n\t.. php:method:: set_tempdata($data[, $value = NULL])\n\n\t\t:param\tmixed\t$data: An array of key/value pairs to set as tempdata, or the key for a single item\n\t\t:param\tmixed\t$value:\tThe value to set for a specific session item, if $data is a key\n\t\t:param\tint\t$ttl: Time-to-live value for the tempdata item(s), in seconds\n\t\t:rtype:\tvoid\n\n\t\tAssigns data to the ``$_SESSION`` superglobal and marks it\n\t\tas \"tempdata\".\n\n\t\t.. note:: This is a legacy method kept only for backwards\n\t\t\tcompatibility with older applications.\n\n\t.. php:method:: sess_regenerate([$destroy = FALSE])\n\n\t\t:param\tbool\t$destroy: Whether to destroy session data\n\t\t:rtype:\tvoid\n\n\t\tRegenerate session ID, optionally destroying the current\n\t\tsession's data.\n\n\t\t.. note:: This method is just an alias for PHP's native\n\t\t\t`session_regenerate_id()\n\t\t\t<https://secure.php.net/session_regenerate_id>`_ function.\n\n\t.. php:method:: sess_destroy()\n\n\t\t:rtype:\tvoid\n\n\t\tDestroys the current session.\n\n\t\t.. note:: This must be the *last* session-related function\n\t\t\tthat you call. All session data will be lost after\n\t\t\tyou do that.\n\n\t\t.. note:: This method is just an alias for PHP's native\n\t\t\t`session_destroy()\n\t\t\t<https://secure.php.net/session_destroy>`_ function.\n\n\t.. php:method:: __get($key)\n\n\t\t:param\tstring\t$key: Session item key\n\t\t:returns:\tThe requested session data item, or NULL if it doesn't exist\n\t\t:rtype:\tmixed\n\n\t\tA magic method that allows you to use\n\t\t``$this->session->item`` instead of ``$_SESSION['item']``,\n\t\tif that's what you prefer.\n\n\t\tIt will also return the session ID by calling\n\t\t``session_id()`` if you try to access\n\t\t``$this->session->session_id``.\n\n\t.. php:method:: __set($key, $value)\n\n\t\t:param\tstring\t$key: Session item key\n\t\t:param\tmixed\t$value: Value to assign to the session item key\n\t\t:returns:\tvoid\n\n\t\tA magic method that allows you to assign items to\n\t\t``$_SESSION`` by accessing them as ``$this->session``\n\t\tproperties::\n\n\t\t\t$this->session->foo = 'bar';\n\n\t\t\t// Results in:\n\t\t\t// $_SESSION['foo'] = 'bar';\n"
  },
  {
    "path": "user_guide_src/source/libraries/table.rst",
    "content": "################\nHTML Table Class\n################\n\nThe Table Class provides functions that enable you to auto-generate HTML\ntables from arrays or database result sets.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n*********************\nUsing the Table Class\n*********************\n\nInitializing the Class\n======================\n\nLike most other classes in CodeIgniter, the Table class is initialized\nin your controller using the ``$this->load->library()`` method::\n\n\t$this->load->library('table');\n\nOnce loaded, the Table library object will be available using::\n\n\t$this->table\n\nExamples\n========\n\nHere is an example showing how you can create a table from a\nmulti-dimensional array. Note that the first array index will become the\ntable heading (or you can set your own headings using the ``set_heading()``\nmethod described in the function reference below).\n\n::\n\n\t$this->load->library('table');\n\n\t$data = array(\n\t\tarray('Name', 'Color', 'Size'),\n\t\tarray('Fred', 'Blue', 'Small'),\n\t\tarray('Mary', 'Red', 'Large'),\n\t\tarray('John', 'Green', 'Medium')\t\n\t);\n\n\techo $this->table->generate($data);\n\nHere is an example of a table created from a database query result. The\ntable class will automatically generate the headings based on the table\nnames (or you can set your own headings using the ``set_heading()``\nmethod described in the class reference below).\n\n::\n\n\t$this->load->library('table');\n\n\t$query = $this->db->query('SELECT * FROM my_table');\n\n\techo $this->table->generate($query);\n\nHere is an example showing how you might create a table using discrete\nparameters::\n\n\t$this->load->library('table');\n\n\t$this->table->set_heading('Name', 'Color', 'Size');\n\n\t$this->table->add_row('Fred', 'Blue', 'Small');\n\t$this->table->add_row('Mary', 'Red', 'Large');\n\t$this->table->add_row('John', 'Green', 'Medium');\n\n\techo $this->table->generate();\n\nHere is the same example, except instead of individual parameters,\narrays are used::\n\n\t$this->load->library('table');\n\n\t$this->table->set_heading(array('Name', 'Color', 'Size'));\n\n\t$this->table->add_row(array('Fred', 'Blue', 'Small'));\n\t$this->table->add_row(array('Mary', 'Red', 'Large'));\n\t$this->table->add_row(array('John', 'Green', 'Medium'));\n\n\techo $this->table->generate();\n\nChanging the Look of Your Table\n===============================\n\nThe Table Class permits you to set a table template with which you can\nspecify the design of your layout. Here is the template prototype::\n\n\t$template = array(\n\t\t'table_open'\t\t=> '<table border=\"0\" cellpadding=\"4\" cellspacing=\"0\">',\n\n\t\t'thead_open'\t\t=> '<thead>',\n\t\t'thead_close'\t\t=> '</thead>',\n\n\t\t'heading_row_start'\t=> '<tr>',\n\t\t'heading_row_end'\t=> '</tr>',\n\t\t'heading_cell_start'\t=> '<th>',\n\t\t'heading_cell_end'\t=> '</th>',\n\n\t\t'tbody_open'\t\t=> '<tbody>',\n\t\t'tbody_close'\t\t=> '</tbody>',\n\n\t\t'row_start'\t\t=> '<tr>',\n\t\t'row_end'\t\t=> '</tr>',\n\t\t'cell_start'\t\t=> '<td>',\n\t\t'cell_end'\t\t=> '</td>',\n\n\t\t'row_alt_start'\t\t=> '<tr>',\n\t\t'row_alt_end'\t\t=> '</tr>',\n\t\t'cell_alt_start'\t=> '<td>',\n\t\t'cell_alt_end'\t\t=> '</td>',\n\n\t\t'table_close'\t\t=> '</table>'\n\t);\n\n\t$this->table->set_template($template);\n\n.. note:: You'll notice there are two sets of \"row\" blocks in the\n\ttemplate. These permit you to create alternating row colors or design\n\telements that alternate with each iteration of the row data.\n\nYou are NOT required to submit a complete template. If you only need to\nchange parts of the layout you can simply submit those elements. In this\nexample, only the table opening tag is being changed::\n\n\t$template = array(\n\t\t'table_open' => '<table border=\"1\" cellpadding=\"2\" cellspacing=\"1\" class=\"mytable\">'\n\t);\n\n\t$this->table->set_template($template);\n\t\nYou can also set defaults for these in a config file.\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Table\n\n\t.. attribute:: $function = NULL\n\n\t\tAllows you to specify a native PHP function or a valid function array object to be applied to all cell data.\n\t\t::\n\n\t\t\t$this->load->library('table');\n\n\t\t\t$this->table->set_heading('Name', 'Color', 'Size');\n\t\t\t$this->table->add_row('Fred', '<strong>Blue</strong>', 'Small');\n\n\t\t\t$this->table->function = 'htmlspecialchars';\n\t\t\techo $this->table->generate();\n\n\t\tIn the above example, all cell data would be ran through PHP's :php:func:`htmlspecialchars()` function, resulting in::\n\n\t\t\t<td>Fred</td><td>&lt;strong&gt;Blue&lt;/strong&gt;</td><td>Small</td>\n\n\t.. php:method:: generate([$table_data = NULL])\n\n\t\t:param\tmixed\t$table_data: Data to populate the table rows with\n\t\t:returns:\tHTML table\n\t\t:rtype:\tstring\n\n\t\tReturns a string containing the generated table. Accepts an optional parameter which can be an array or a database result object.\n\n\t.. php:method:: set_caption($caption)\n\n\t\t:param\tstring\t$caption: Table caption\n\t\t:returns:\tCI_Table instance (method chaining)\n\t\t:rtype:\tCI_Table\n\n\t\tPermits you to add a caption to the table.\n\t\t::\n\n\t\t\t$this->table->set_caption('Colors');\n\n\t.. php:method:: set_heading([$args = array()[, ...]])\n\n\t\t:param\tmixed\t$args: An array or multiple strings containing the table column titles\n\t\t:returns:\tCI_Table instance (method chaining)\n\t\t:rtype:\tCI_Table\n\n\t\tPermits you to set the table heading. You can submit an array or discrete params::\n\n\t\t\t$this->table->set_heading('Name', 'Color', 'Size');\n\n\t\t\t$this->table->set_heading(array('Name', 'Color', 'Size'));\n\n\t.. php:method:: add_row([$args = array()[, ...]])\n\n\t\t:param\tmixed\t$args: An array or multiple strings containing the row values\n\t\t:returns:\tCI_Table instance (method chaining)\n\t\t:rtype:\tCI_Table\n\n\t\tPermits you to add a row to your table. You can submit an array or discrete params::\n\n\t\t\t$this->table->add_row('Blue', 'Red', 'Green');\n\n\t\t\t$this->table->add_row(array('Blue', 'Red', 'Green'));\n\n\t\tIf you would like to set an individual cell's tag attributes, you can use an associative array for that cell.\n\t\tThe associative key **data** defines the cell's data. Any other key => val pairs are added as key='val' attributes to the tag::\n\n\t\t\t$cell = array('data' => 'Blue', 'class' => 'highlight', 'colspan' => 2);\n\t\t\t$this->table->add_row($cell, 'Red', 'Green');\n\n\t\t\t// generates\n\t\t\t// <td class='highlight' colspan='2'>Blue</td><td>Red</td><td>Green</td>\n\n\t.. php:method:: make_columns([$array = array()[, $col_limit = 0]])\n\n\t\t:param\tarray\t$array: An array containing multiple rows' data\n\t\t:param\tint\t$col_limit: Count of columns in the table\n\t\t:returns:\tAn array of HTML table columns\n\t\t:rtype:\tarray\n\n\t\tThis method takes a one-dimensional array as input and creates a multi-dimensional array with a depth equal to the number of columns desired.\n\t\tThis allows a single array with many elements to be displayed in a table that has a fixed column count. Consider this example::\n\n\t\t\t$list = array('one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve');\n\n\t\t\t$new_list = $this->table->make_columns($list, 3);\n\n\t\t\t$this->table->generate($new_list);\n\n\t\t\t// Generates a table with this prototype\n\n\t\t\t<table border=\"0\" cellpadding=\"4\" cellspacing=\"0\">\n\t\t\t<tr>\n\t\t\t<td>one</td><td>two</td><td>three</td>\n\t\t\t</tr><tr>\n\t\t\t<td>four</td><td>five</td><td>six</td>\n\t\t\t</tr><tr>\n\t\t\t<td>seven</td><td>eight</td><td>nine</td>\n\t\t\t</tr><tr>\n\t\t\t<td>ten</td><td>eleven</td><td>twelve</td></tr>\n\t\t\t</table>\n\n\n\t.. php:method:: set_template($template)\n\n\t\t:param\tarray\t$template: An associative array containing template values\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tPermits you to set your template. You can submit a full or partial template.\n\t\t::\n\n\t\t\t$template = array(\n\t\t\t\t'table_open'  => '<table border=\"1\" cellpadding=\"2\" cellspacing=\"1\" class=\"mytable\">'\n\t\t\t);\n\t\t\n\t\t\t$this->table->set_template($template);\n\n\t.. php:method:: set_empty($value)\n\n\t\t:param\tmixed\t$value: Value to put in empty cells\n\t\t:returns:\tCI_Table instance (method chaining)\n\t\t:rtype:\tCI_Table\n\n\t\tLets you set a default value for use in any table cells that are empty.\n\t\tYou might, for example, set a non-breaking space::\n\n\t\t\t$this->table->set_empty(\"&nbsp;\");\n\n\t.. php:method:: clear()\n\n\t\t:returns:\tCI_Table instance (method chaining)\n\t\t:rtype:\tCI_Table\n\n\t\tLets you clear the table heading, row data and caption. If\n\t\tyou need to show multiple tables with different data you\n\t\tshould to call this method after each table has been\n\t\tgenerated to clear the previous table information.\n\n\t\tExample ::\n\n\t\t\t$this->load->library('table');\n\n\t\t\t$this->table->set_caption('Preferences');\n\t\t\t$this->table->set_heading('Name', 'Color', 'Size');\n\t\t\t$this->table->add_row('Fred', 'Blue', 'Small');\n\t\t\t$this->table->add_row('Mary', 'Red', 'Large');\n\t\t\t$this->table->add_row('John', 'Green', 'Medium');\n\n\t\t\techo $this->table->generate();\n\n\t\t\t$this->table->clear();\n\n\t\t\t$this->table->set_caption('Shipping');\n\t\t\t$this->table->set_heading('Name', 'Day', 'Delivery');\n\t\t\t$this->table->add_row('Fred', 'Wednesday', 'Express');\n\t\t\t$this->table->add_row('Mary', 'Monday', 'Air');\n\t\t\t$this->table->add_row('John', 'Saturday', 'Overnight');\n\n\t\t\techo $this->table->generate();\n"
  },
  {
    "path": "user_guide_src/source/libraries/trackback.rst",
    "content": "###############\nTrackback Class\n###############\n\nThe Trackback Class provides functions that enable you to send and\nreceive Trackback data.\n\nIf you are not familiar with Trackbacks you'll find more information\n`here <https://en.wikipedia.org/wiki/Trackback>`_.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n*************************\nUsing the Trackback Class\n*************************\n\nInitializing the Class\n======================\n\nLike most other classes in CodeIgniter, the Trackback class is\ninitialized in your controller using the ``$this->load->library()`` method::\n\n\t$this->load->library('trackback');\n\nOnce loaded, the Trackback library object will be available using::\n\n\t$this->trackback\n\nSending Trackbacks\n==================\n\nA Trackback can be sent from any of your controller functions using code\nsimilar to this example::\n\n\t$this->load->library('trackback');\n\n\t$tb_data = array(\n\t\t'ping_url'  => 'http://example.com/trackback/456',\n\t\t'url'       => 'http://www.my-example.com/blog/entry/123',\n\t\t'title'     => 'The Title of My Entry',\n\t\t'excerpt'   => 'The entry content.',\n\t\t'blog_name' => 'My Blog Name',\n\t\t'charset'   => 'utf-8'\n\t);\n\n\tif ( ! $this->trackback->send($tb_data))\n\t{\n\t\techo $this->trackback->display_errors();\n\t}\n\telse\n\t{\n\t\techo 'Trackback was sent!';\n\t}\n\nDescription of array data:\n\n-  **ping_url** - The URL of the site you are sending the Trackback to.\n   You can send Trackbacks to multiple URLs by separating each URL with a comma.\n-  **url** - The URL to YOUR site where the weblog entry can be seen.\n-  **title** - The title of your weblog entry.\n-  **excerpt** - The content of your weblog entry.\n-  **blog_name** - The name of your weblog.\n-  **charset** - The character encoding your weblog is written in. If omitted, UTF-8 will be used.\n\n.. note:: The Trackback class will automatically send only the first 500 characters of your \n\tentry. It will also strip all HTML.\n\nThe Trackback sending method returns TRUE/FALSE (boolean) on success\nor failure. If it fails, you can retrieve the error message using::\n\n\t$this->trackback->display_errors();\n\nReceiving Trackbacks\n====================\n\nBefore you can receive Trackbacks you must create a weblog. If you don't\nhave a blog yet there's no point in continuing.\n\nReceiving Trackbacks is a little more complex than sending them, only\nbecause you will need a database table in which to store them, and you\nwill need to validate the incoming trackback data. You are encouraged to\nimplement a thorough validation process to guard against spam and\nduplicate data. You may also want to limit the number of Trackbacks you\nallow from a particular IP within a given span of time to further\ncurtail spam. The process of receiving a Trackback is quite simple; the\nvalidation is what takes most of the effort.\n\nYour Ping URL\n=============\n\nIn order to accept Trackbacks you must display a Trackback URL next to\neach one of your weblog entries. This will be the URL that people will\nuse to send you Trackbacks (we will refer to this as your \"Ping URL\").\n\nYour Ping URL must point to a controller function where your Trackback\nreceiving code is located, and the URL must contain the ID number for\neach particular entry, so that when the Trackback is received you'll be\nable to associate it with a particular entry.\n\nFor example, if your controller class is called Trackback, and the\nreceiving function is called receive, your Ping URLs will look something\nlike this::\n\n\thttp://example.com/index.php/trackback/receive/entry_id\n\nWhere entry_id represents the individual ID number for each of your\nentries.\n\nCreating a Trackback Table\n==========================\n\nBefore you can receive Trackbacks you must create a table in which to\nstore them. Here is a basic prototype for such a table::\n\n\tCREATE TABLE trackbacks (\n\t\ttb_id int(10) unsigned NOT NULL auto_increment,\n\t\tentry_id int(10) unsigned NOT NULL default 0,\n\t\turl varchar(200) NOT NULL,\n\t\ttitle varchar(100) NOT NULL,\n\t\texcerpt text NOT NULL,\n\t\tblog_name varchar(100) NOT NULL,\n\t\ttb_date int(10) NOT NULL,\n\t\tip_address varchar(45) NOT NULL,\n\t\tPRIMARY KEY `tb_id` (`tb_id`),\n\t\tKEY `entry_id` (`entry_id`)\n\t);\n\nThe Trackback specification only requires four pieces of information to\nbe sent in a Trackback (url, title, excerpt, blog_name), but to make\nthe data more useful we've added a few more fields in the above table\nschema (date, IP address, etc.).\n\nProcessing a Trackback\n======================\n\nHere is an example showing how you will receive and process a Trackback.\nThe following code is intended for use within the controller function\nwhere you expect to receive Trackbacks.::\n\n\t$this->load->library('trackback');\n\t$this->load->database();\n\n\tif ($this->uri->segment(3) == FALSE)\n\t{\n\t\t$this->trackback->send_error('Unable to determine the entry ID');\n\t}\n\n\tif ( ! $this->trackback->receive())\n\t{\n\t\t$this->trackback->send_error('The Trackback did not contain valid data');\n\t}\n\n\t$data = array(\n\t\t'tb_id'      => '',\n\t\t'entry_id'   => $this->uri->segment(3),\n\t\t'url'        => $this->trackback->data('url'),\n\t\t'title'      => $this->trackback->data('title'),\n\t\t'excerpt'    => $this->trackback->data('excerpt'),\n\t\t'blog_name'  => $this->trackback->data('blog_name'),\n\t\t'tb_date'    => time(),\n\t\t'ip_address' => $this->input->ip_address()\n\t);\n\n\t$sql = $this->db->insert_string('trackbacks', $data);\n\t$this->db->query($sql);\n\n\t$this->trackback->send_success();\n\nNotes:\n^^^^^^\n\nThe entry ID number is expected in the third segment of your URL. This\nis based on the URI example we gave earlier::\n\n\thttp://example.com/index.php/trackback/receive/entry_id\n\nNotice the entry_id is in the third URI segment, which you can retrieve\nusing::\n\n\t$this->uri->segment(3);\n\nIn our Trackback receiving code above, if the third segment is missing,\nwe will issue an error. Without a valid entry ID, there's no reason to\ncontinue.\n\nThe $this->trackback->receive() function is simply a validation function\nthat looks at the incoming data and makes sure it contains the four\npieces of data that are required (url, title, excerpt, blog_name). It\nreturns TRUE on success and FALSE on failure. If it fails you will issue\nan error message.\n\nThe incoming Trackback data can be retrieved using this function::\n\n\t$this->trackback->data('item')\n\nWhere item represents one of these four pieces of info: url, title,\nexcerpt, or blog_name\n\nIf the Trackback data is successfully received, you will issue a success\nmessage using::\n\n\t$this->trackback->send_success();\n\n.. note:: The above code contains no data validation, which you are\n\tencouraged to add.\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Trackback\n\n\t.. attribute:: $data = array('url' => '', 'title' => '', 'excerpt' => '', 'blog_name' => '', 'charset' => '')\n\n\t\tTrackback data array.\n\n\t.. attribute:: $convert_ascii = TRUE\n\n\t\tWhether to convert high ASCII and MS Word characters to HTML entities.\n\n\t.. php:method:: send($tb_data)\n\n\t\t:param\tarray\t$tb_data: Trackback data\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tSend trackback.\n\n\t.. php:method:: receive()\n\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tThis method simply validates the incoming TB data, returning TRUE on success and FALSE on failure.\n\t\tIf the data is valid it is set to the ``$this->data`` array so that it can be inserted into a database.\n\n\t.. php:method:: send_error([$message = 'Incomplete information'])\n\n\t\t:param\tstring\t$message: Error message\n\t\t:rtype: void\n\n\t\tResponses to a trackback request with an error message.\n\n\t\t.. note:: This method will terminate script execution.\n\n\t.. php:method:: send_success()\n\n\t\t:rtype:\tvoid\n\n\t\tResponses to a trackback request with a success message.\n\n\t\t.. note:: This method will terminate script execution.\n\n\t.. php:method:: data($item)\n\n\t\t:param\tstring\t$item: Data key\n\t\t:returns:\tData value or empty string if not found\n\t\t:rtype:\tstring\n\n\t\tReturns a single item from the response data array.\n\n\t.. php:method:: process($url, $data)\n\n\t\t:param\tstring\t$url: Target url\n\t\t:param\tstring\t$data: Raw POST data\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tOpens a socket connection and passes the data to the server, returning TRUE on success and FALSE on failure.\n\n\t.. php:method:: extract_urls($urls)\n\n\t\t:param\tstring\t$urls: Comma-separated URL list\n\t\t:returns:\tArray of URLs\n\t\t:rtype:\tarray\n\n\t\tThis method lets multiple trackbacks to be sent. It takes a string of URLs (separated by comma or space) and puts each URL into an array.\n\n\t.. php:method:: validate_url(&$url)\n\n\t\t:param\tstring\t$url: Trackback URL\n\t\t:rtype:\tvoid\n\n\t\tSimply adds the *http://* prefix it it's not already present in the URL.\n\n\t.. php:method:: get_id($url)\n\n\t\t:param\tstring\t$url: Trackback URL\n\t\t:returns:\tURL ID or FALSE on failure\n\t\t:rtype:\tstring\n\n\t\tFind and return a trackback URL's ID or FALSE on failure.\n\n\t.. php:method:: convert_xml($str)\n\n\t\t:param\tstring\t$str: Input string\n\t\t:returns:\tConverted string\n\t\t:rtype:\tstring\n\n\t\tConverts reserved XML characters to entities.\n\n\t.. php:method:: limit_characters($str[, $n = 500[, $end_char = '&#8230;']])\n\n\t\t:param\tstring\t$str: Input string\n\t\t:param\tint\t$n: Max characters number\n\t\t:param\tstring\t$end_char: Character to put at end of string\n\t\t:returns:\tShortened string\n\t\t:rtype:\tstring\n\n\t\tLimits the string based on the character count. Will preserve complete words.\n\n\t.. php:method:: convert_ascii($str)\n\n\t\t:param\tstring\t$str: Input string\n\t\t:returns:\tConverted string\n\t\t:rtype:\tstring\n\n\t\tConverts high ASCII text and MS Word special characterss to HTML entities.\n\n\t.. php:method:: set_error($msg)\n\n\t\t:param\tstring\t$msg: Error message\n\t\t:rtype:\tvoid\n\n\t\tSet an log an error message.\n\n\t.. php:method:: display_errors([$open = '<p>'[, $close = '</p>']])\n\n\t\t:param\tstring\t$open: Open tag\n\t\t:param\tstring\t$close: Close tag\n\t\t:returns:\tHTML formatted error messages\n\t\t:rtype:\tstring\n\n\t\tReturns error messages formatted in HTML or an empty string if there are no errors."
  },
  {
    "path": "user_guide_src/source/libraries/typography.rst",
    "content": "################\nTypography Class\n################\n\nThe Typography Class provides methods that help you format text.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n**************************\nUsing the Typography Class\n**************************\n\nInitializing the Class\n======================\n\nLike most other classes in CodeIgniter, the Typography class is\ninitialized in your controller using the ``$this->load->library()`` method::\n\n\t$this->load->library('typography');\n\nOnce loaded, the Typography library object will be available using::\n\n\t$this->typography\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Typography\n\n\t.. attribute:: $protect_braced_quotes = FALSE\n\n\t\tWhen using the Typography library in conjunction with the :doc:`Template Parser library <parser>`\n\t\tit can often be desirable to protect single and double quotes within curly braces.\n\t\tTo enable this, set the ``protect_braced_quotes`` class property to TRUE.\n\n\t\tUsage example::\n\n\t\t\t$this->load->library('typography');\n\t\t\t$this->typography->protect_braced_quotes = TRUE;\n\n\t.. php:method:: auto_typography($str[, $reduce_linebreaks = FALSE])\n\n\t\t:param\tstring\t$str: Input string\n\t\t:param\tbool\t$reduce_linebreaks: Whether to reduce consecutive linebreaks\n\t\t:returns:\tHTML typography-safe string\n\t\t:rtype:\tstring\n\n\t\tFormats text so that it is semantically and typographically correct HTML.\n\t\tTakes a string as input and returns it with the following formatting:\n\n\t\t -  Surrounds paragraphs within <p></p> (looks for double line breaks to identify paragraphs).\n\t\t -  Single line breaks are converted to <br />, except those that appear within <pre> tags.\n\t\t -  Block level elements, like <div> tags, are not wrapped within paragraphs, but their contained text is if it contains paragraphs.\n\t\t -  Quotes are converted to correctly facing curly quote entities, except those that appear within tags.\n\t\t -  Apostrophes are converted to curly apostrophe entities.\n\t\t -  Double dashes (either like -- this or like--this) are converted to em—dashes.\n\t\t -  Three consecutive periods either preceding or following a word are converted to ellipsis (…).\n\t\t -  Double spaces following sentences are converted to non-breaking spaces to mimic double spacing.\n\n\t\tUsage example::\n\n\t\t\t$string = $this->typography->auto_typography($string);\n\n\t\tThere is one optional parameter that determines whether the parser should reduce more than two consecutive line breaks down to two.\n\t\tPass boolean TRUE to enable reducing line breaks::\n\n\t\t\t$string = $this->typography->auto_typography($string, TRUE);\n\n\t\t.. note:: Typographic formatting can be processor intensive, particularly if you have a lot of content being formatted.\n\t\t\tIf you choose to use this method you may want to consider :doc:`caching <../general/caching>` your pages.\n\n\t.. php:method:: format_characters($str)\n\n\t\t:param\tstring\t$str: Input string\n\t\t:returns:\tFormatted string\n\t\t:rtype:\tstring\n\n\t\tThis method is similar to ``auto_typography()`` above, except that it only does character conversion:\n\n\t\t -  Quotes are converted to correctly facing curly quote entities, except those that appear within tags.\n\t\t -  Apostrophes are converted to curly apostrophe entities.\n\t\t -  Double dashes (either like -- this or like--this) are converted to em—dashes.\n\t\t -  Three consecutive periods either preceding or following a word are converted to ellipsis (…).\n\t\t -  Double spaces following sentences are converted to non-breaking spaces to mimic double spacing.\n\n\t\tUsage example::\n\n\t\t\t$string = $this->typography->format_characters($string);\n\n\t.. php:method:: nl2br_except_pre($str)\n\n\t\t:param\tstring\t$str: Input string\n\t\t:returns:\tFormatted string\n\t\t:rtype:\tstring\n\n\t\tConverts newlines to <br /> tags unless they appear within <pre> tags.\n\t\tThis method is identical to the native PHP :php:func:`nl2br()` function, except that it ignores <pre> tags.\n\n\t\tUsage example::\n\n\t\t\t$string = $this->typography->nl2br_except_pre($string);\n"
  },
  {
    "path": "user_guide_src/source/libraries/unit_testing.rst",
    "content": "##################\nUnit Testing Class\n##################\n\nUnit testing is an approach to software development in which tests are\nwritten for each function in your application. If you are not familiar\nwith the concept you might do a little googling on the subject.\n\nCodeIgniter's Unit Test class is quite simple, consisting of an\nevaluation function and two result functions. It's not intended to be a\nfull-blown test suite but rather a simple mechanism to evaluate your\ncode to determine if it is producing the correct data type and result.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n******************************\nUsing the Unit Testing Library\n******************************\n\nInitializing the Class\n======================\n\nLike most other classes in CodeIgniter, the Unit Test class is\ninitialized in your controller using the $this->load->library function::\n\n\t$this->load->library('unit_test');\n\nOnce loaded, the Unit Test object will be available using ``$this->unit``\n\nRunning Tests\n=============\n\nRunning a test involves supplying a test and an expected result in the\nfollowing way:\n\n\t$this->unit->run('test', 'expected result', 'test name', 'notes');\n\nWhere test is the result of the code you wish to test, expected result\nis the data type you expect, test name is an optional name you can give\nyour test, and notes are optional notes. Example::\n\n\t$test = 1 + 1;\n\n\t$expected_result = 2;\n\n\t$test_name = 'Adds one plus one';\n\n\t$this->unit->run($test, $expected_result, $test_name);\n\nThe expected result you supply can either be a literal match, or a data\ntype match. Here's an example of a literal::\n\n\t$this->unit->run('Foo', 'Foo');\n\nHere is an example of a data type match::\n\n\t$this->unit->run('Foo', 'is_string');\n\nNotice the use of \"is_string\" in the second parameter? This tells the\nfunction to evaluate whether your test is producing a string as the\nresult. Here is a list of allowed comparison types:\n\n-  is_object\n-  is_string\n-  is_bool\n-  is_true\n-  is_false\n-  is_int\n-  is_numeric\n-  is_float\n-  is_double\n-  is_array\n-  is_null\n-  is_resource\n\nGenerating Reports\n==================\n\nYou can either display results after each test, or your can run several\ntests and generate a report at the end. To show a report directly simply\necho or return the run function::\n\n\techo $this->unit->run($test, $expected_result);\n\nTo run a full report of all tests, use this::\n\n\techo $this->unit->report();\n\nThe report will be formatted in an HTML table for viewing. If you prefer\nthe raw data you can retrieve an array using::\n\n\techo $this->unit->result();\n\nStrict Mode\n===========\n\nBy default the unit test class evaluates literal matches loosely.\nConsider this example::\n\n\t$this->unit->run(1, TRUE);\n\nThe test is evaluating an integer, but the expected result is a boolean.\nPHP, however, due to it's loose data-typing will evaluate the above code\nas TRUE using a normal equality test::\n\n\tif (1 == TRUE) echo 'This evaluates as true';\n\nIf you prefer, you can put the unit test class in to strict mode, which\nwill compare the data type as well as the value::\n\n\tif (1 === TRUE) echo 'This evaluates as FALSE';\n\nTo enable strict mode use this::\n\n\t$this->unit->use_strict(TRUE);\n\nEnabling/Disabling Unit Testing\n===============================\n\nIf you would like to leave some testing in place in your scripts, but\nnot have it run unless you need it, you can disable unit testing using::\n\n\t$this->unit->active(FALSE);\n\nUnit Test Display\n=================\n\nWhen your unit test results display, the following items show by\ndefault:\n\n-  Test Name (test_name)\n-  Test Datatype (test_datatype)\n-  Expected Datatype (res_datatype)\n-  Result (result)\n-  File Name (file)\n-  Line Number (line)\n-  Any notes you entered for the test (notes)\n\nYou can customize which of these items get displayed by using\n$this->unit->set_test_items(). For example, if you only wanted the test name\nand the result displayed:\n\nCustomizing displayed tests\n---------------------------\n\n::\n\n\t$this->unit->set_test_items(array('test_name', 'result'));\n\nCreating a Template\n-------------------\n\nIf you would like your test results formatted differently then the\ndefault you can set your own template. Here is an example of a simple\ntemplate. Note the required pseudo-variables::\n\n\t$str = '\n\t<table border=\"0\" cellpadding=\"4\" cellspacing=\"1\">\n\t{rows}\n\t\t<tr>\n\t\t\t<td>{item}</td>\n\t\t\t<td>{result}</td>\n\t\t</tr>\n\t{/rows}\n\t</table>';\n\n\t$this->unit->set_template($str);\n\n.. note:: Your template must be declared **before** running the unit\n\ttest process.\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Unit_test\n\n\t.. php:method:: set_test_items($items)\n\n\t\t:param array $items: List of visible test items\n\t\t:returns: void\n\n\t\tSets a list of items that should be visible in tests.\n\t\tValid options are:\n\n\t\t  - test_name\n\t\t  - test_datatype\n\t\t  - res_datatype\n\t\t  - result\n\t\t  - file\n\t\t  - line\n\t\t  - notes\n\n\t.. php:method:: run($test[, $expected = TRUE[, $test_name = 'undefined'[, $notes = '']]])\n\n\t\t:param\tmixed\t$test: Test data\n\t\t:param\tmixed\t$expected: Expected result\n\t\t:param\tstring\t$test_name: Test name\n\t\t:param\tstring\t$notes: Any notes to be attached to the test\n\t\t:returns:\tTest report\n\t\t:rtype:\tstring\n\n\t\tRuns unit tests.\n\n\t.. php:method:: report([$result = array()])\n\n\t\t:param\tarray\t$result: Array containing tests results\n\t\t:returns:\tTest report\n\t\t:rtype:\tstring\n\n\t\tGenerates a report about already complete tests.\n\n\t.. php:method:: use_strict([$state = TRUE])\n\n\t\t:param\tbool\t$state: Strict state flag\n\t\t:rtype:\tvoid\n\n\t\tEnables/disables strict type comparison in tests.\n\n\t.. php:method:: active([$state = TRUE])\n\n\t\t:param\tbool\t$state: Whether to enable testing\n\t\t:rtype:\tvoid\n\n\t\tEnables/disables unit testing.\n\n\t.. php:method:: result([$results = array()])\n\n\t\t:param\tarray\t$results: Tests results list\n\t\t:returns:\tArray of raw result data\n\t\t:rtype:\tarray\n\n\t\tReturns raw tests results data.\n\n\t.. php:method:: set_template($template)\n\n\t\t:param\tstring\t$template: Test result template\n\t\t:rtype:\tvoid\n\n\t\tSets the template for displaying tests results."
  },
  {
    "path": "user_guide_src/source/libraries/uri.rst",
    "content": "#########\nURI Class\n#########\n\nThe URI Class provides methods that help you retrieve information from\nyour URI strings. If you use URI routing, you can also retrieve\ninformation about the re-routed segments.\n\n.. note:: This class is initialized automatically by the system so there\n\tis no need to do it manually.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_URI\n\n\t.. php:method:: segment($n[, $no_result = NULL])\n\n\t\t:param\tint\t$n: Segment index number\n\t\t:param\tmixed\t$no_result: What to return if the searched segment is not found\n\t\t:returns:\tSegment value or $no_result value if not found\n\t\t:rtype:\tmixed\n\n\t\tPermits you to retrieve a specific segment. Where n is the segment\n\t\tnumber you wish to retrieve. Segments are numbered from left to right.\n\t\tFor example, if your full URL is this::\n\n\t\t\thttp://example.com/index.php/news/local/metro/crime_is_up\n\n\t\tThe segment numbers would be this:\n\n\t\t#. news\n\t\t#. local\n\t\t#. metro\n\t\t#. crime_is_up\n\n\t\tThe optional second parameter defaults to NULL and allows you to set the return value\n\t\tof this method when the requested URI segment is missing.\n\t\tFor example, this would tell the method to return the number zero in the event of failure::\n\n\t\t\t$product_id = $this->uri->segment(3, 0);\n\n\t\tIt helps avoid having to write code like this::\n\n\t\t\tif ($this->uri->segment(3) === FALSE)\n\t\t\t{\n\t\t\t\t$product_id = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$product_id = $this->uri->segment(3);\n\t\t\t}\n\n\t.. php:method:: rsegment($n[, $no_result = NULL])\n\n\t\t:param\tint\t$n: Segment index number\n\t\t:param\tmixed\t$no_result: What to return if the searched segment is not found\n\t\t:returns:\tRouted segment value or $no_result value if not found\n\t\t:rtype:\tmixed\n\n\t\tThis method is identical to ``segment()``, except that it lets you retrieve\n\t\ta specific segment from your re-routed URI in the event you are\n\t\tusing CodeIgniter's :doc:`URI Routing <../general/routing>` feature.\n\n\t.. php:method:: slash_segment($n[, $where = 'trailing'])\n\n\t\t:param\tint\t$n: Segment index number\n\t\t:param\tstring\t$where: Where to add the slash ('trailing' or 'leading')\n\t\t:returns:\tSegment value, prepended/suffixed with a forward slash, or a slash if not found\n\t\t:rtype:\tstring\n\n\t\tThis method is almost identical to ``segment()``, except it\n\t\tadds a trailing and/or leading slash based on the second parameter.\n\t\tIf the parameter is not used, a trailing slash added. Examples::\n\n\t\t\t$this->uri->slash_segment(3);\n\t\t\t$this->uri->slash_segment(3, 'leading');\n\t\t\t$this->uri->slash_segment(3, 'both');\n\n\t\tReturns:\n\n\t\t#. segment/\n\t\t#. /segment\n\t\t#. /segment/\n\n\t.. php:method:: slash_rsegment($n[, $where = 'trailing'])\n\n\t\t:param\tint\t$n: Segment index number\n\t\t:param\tstring\t$where: Where to add the slash ('trailing' or 'leading')\n\t\t:returns:\tRouted segment value, prepended/suffixed with a forward slash, or a slash if not found\n\t\t:rtype:\tstring\n\n\t\tThis method is identical to ``slash_segment()``, except that it lets you\n\t\tadd slashes a specific segment from your re-routed URI in the event you\n\t\tare using CodeIgniter's :doc:`URI Routing <../general/routing>`\n\t\tfeature.\n\n\t.. php:method:: uri_to_assoc([$n = 3[, $default = array()]])\n\n\t\t:param\tint\t$n: Segment index number\n\t\t:param\tarray\t$default: Default values\n\t\t:returns:\tAssociative URI segments array\n\t\t:rtype:\tarray\n\n\t\tThis method lets you turn URI segments into an associative array of\n\t\tkey/value pairs. Consider this URI::\n\n\t\t\tindex.php/user/search/name/joe/location/UK/gender/male\n\n\t\tUsing this method you can turn the URI into an associative array with\n\t\tthis prototype::\n\n\t\t\t[array]\n\t\t\t(\n\t\t\t\t'name'\t\t=> 'joe'\n\t\t\t\t'location'\t=> 'UK'\n\t\t\t\t'gender'\t=> 'male'\n\t\t\t)\n\n\t\tThe first parameter lets you set an offset, which defaults to 3 since your\n\t\tURI will normally contain a controller/method pair in the first and second segments.\n\t\tExample::\n\n\t\t\t$array = $this->uri->uri_to_assoc(3);\n\t\t\techo $array['name'];\n\n\t\tThe second parameter lets you set default key names, so that the array\n\t\treturned will always contain expected indexes, even if missing from the URI.\n\t\tExample::\n\n\t\t\t$default = array('name', 'gender', 'location', 'type', 'sort');\n\t\t\t$array = $this->uri->uri_to_assoc(3, $default);\n\n\t\tIf the URI does not contain a value in your default, an array index will\n\t\tbe set to that name, with a value of NULL.\n\n\t\tLastly, if a corresponding value is not found for a given key (if there\n\t\tis an odd number of URI segments) the value will be set to NULL.\n\n\t.. php:method:: ruri_to_assoc([$n = 3[, $default = array()]])\n\n\t\t:param\tint\t$n: Segment index number\n\t\t:param\tarray\t$default: Default values\n\t\t:returns:\tAssociative routed URI segments array\n\t\t:rtype:\tarray\n\n\t\tThis method is identical to ``uri_to_assoc()``, except that it creates\n\t\tan associative array using the re-routed URI in the event you are using\n\t\tCodeIgniter's :doc:`URI Routing <../general/routing>` feature.\n\n\t.. php:method:: assoc_to_uri($array)\n\n\t\t:param\tarray\t$array: Input array of key/value pairs\n\t\t:returns:\tURI string\n\t\t:rtype:\tstring\n\n\t\tTakes an associative array as input and generates a URI string from it.\n\t\tThe array keys will be included in the string. Example::\n\n\t\t\t$array = array('product' => 'shoes', 'size' => 'large', 'color' => 'red');\n\t\t\t$str = $this->uri->assoc_to_uri($array);\n\n\t\t\t// Produces: product/shoes/size/large/color/red\n\n\t.. php:method:: uri_string()\n\n\t\t:returns:\tURI string\n\t\t:rtype:\tstring\n\n\t\tReturns a string with the complete URI. For example, if this is your full URL::\n\n\t\t\thttp://example.com/index.php/news/local/345\n\n\t\tThe method would return this::\n\n\t\t\tnews/local/345\n\n\t.. php:method:: ruri_string()\n\n\t\t:returns:\tRouted URI string\n\t\t:rtype:\tstring\n\n\t\tThis method is identical to ``uri_string()``, except that it returns\n\t\tthe re-routed URI in the event you are using CodeIgniter's :doc:`URI\n\t\tRouting <../general/routing>` feature.\n\n\t.. php:method:: total_segments()\n\n\t\t:returns:\tCount of URI segments\n\t\t:rtype:\tint\n\n\t\tReturns the total number of segments.\n\n\t.. php:method:: total_rsegments()\n\n\t\t:returns:\tCount of routed URI segments\n\t\t:rtype:\tint\n\n\t\tThis method is identical to ``total_segments()``, except that it returns\n\t\tthe total number of segments in your re-routed URI in the event you are\n\t\tusing CodeIgniter's :doc:`URI Routing <../general/routing>` feature.\n\n\t.. php:method:: segment_array()\n\n\t\t:returns:\tURI segments array\n\t\t:rtype:\tarray\n\n\t\tReturns an array containing the URI segments. For example::\n\n\t\t\t$segs = $this->uri->segment_array();\n\n\t\t\tforeach ($segs as $segment)\n\t\t\t{\n\t\t\t\techo $segment;\n\t\t\t\techo '<br />';\n\t\t\t}\n\n\t.. php:method:: rsegment_array()\n\n\t\t:returns:\tRouted URI segments array\n\t\t:rtype:\tarray\n\n\t\tThis method is identical to ``segment_array()``, except that it returns\n\t\tthe array of segments in your re-routed URI in the event you are using\n\t\tCodeIgniter's :doc:`URI Routing <../general/routing>` feature.\n"
  },
  {
    "path": "user_guide_src/source/libraries/user_agent.rst",
    "content": "################\nUser Agent Class\n################\n\nThe User Agent Class provides functions that help identify information\nabout the browser, mobile device, or robot visiting your site. In\naddition you can get referrer information as well as language and\nsupported character-set information.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n**************************\nUsing the User Agent Class\n**************************\n\nInitializing the Class\n======================\n\nLike most other classes in CodeIgniter, the User Agent class is\ninitialized in your controller using the $this->load->library function::\n\n\t$this->load->library('user_agent');\n\nOnce loaded, the object will be available using: ``$this->agent``\n\nUser Agent Definitions\n======================\n\nThe user agent name definitions are located in a config file located at:\napplication/config/user_agents.php. You may add items to the various\nuser agent arrays if needed.\n\nExample\n=======\n\nWhen the User Agent class is initialized it will attempt to determine\nwhether the user agent browsing your site is a web browser, a mobile\ndevice, or a robot. It will also gather the platform information if it\nis available.\n\n::\n\n\t$this->load->library('user_agent');\n\n\tif ($this->agent->is_browser())\n\t{\n\t\t$agent = $this->agent->browser().' '.$this->agent->version();\n\t}\n\telseif ($this->agent->is_robot())\n\t{\n\t\t$agent = $this->agent->robot();\n\t}\n\telseif ($this->agent->is_mobile())\n\t{\n\t\t$agent = $this->agent->mobile();\n\t}\n\telse\n\t{\n\t\t$agent = 'Unidentified User Agent';\n\t}\n\n\techo $agent;\n\n\techo $this->agent->platform(); // Platform info (Windows, Linux, Mac, etc.)\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_User_agent\n\n\t.. php:method:: is_browser([$key = NULL])\n\n\t\t:param\tstring\t$key: Optional browser name\n\t\t:returns:\tTRUE if the user agent is a (specified) browser, FALSE if not\n\t\t:rtype:\tbool\n\n\t\tReturns TRUE/FALSE (boolean) if the user agent is a known web browser.\n\t\t::\n\n\t\t\tif ($this->agent->is_browser('Safari'))\n\t\t\t{\n\t\t\t\techo 'You are using Safari.';\n\t\t\t}\n\t\t\telseif ($this->agent->is_browser())\n\t\t\t{\n\t\t\t\techo 'You are using a browser.';\n\t\t\t}\n\n\t\t.. note:: The string \"Safari\" in this example is an array key in the list of browser definitions.\n\t\t\tYou can find this list in **application/config/user_agents.php** if you want to add new\n\t\t\tbrowsers or change the stings.\n\n\t.. php:method:: is_mobile([$key = NULL])\n\n\t\t:param\tstring\t$key: Optional mobile device name\n\t\t:returns:\tTRUE if the user agent is a (specified) mobile device, FALSE if not\n\t\t:rtype:\tbool\n\n\t\tReturns TRUE/FALSE (boolean) if the user agent is a known mobile device.\n\t\t::\n\n\t\t\tif ($this->agent->is_mobile('iphone'))\n\t\t\t{\n\t\t\t\t$this->load->view('iphone/home');\n\t\t\t}\n\t\t\telseif ($this->agent->is_mobile())\n\t\t\t{\n\t\t\t\t$this->load->view('mobile/home');\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$this->load->view('web/home');\n\t\t\t}\n\n\t.. php:method:: is_robot([$key = NULL])\n\n\t\t:param\tstring\t$key: Optional robot name\n\t\t:returns:\tTRUE if the user agent is a (specified) robot, FALSE if not\n\t\t:rtype:\tbool\n\n\t\tReturns TRUE/FALSE (boolean) if the user agent is a known robot.\n\n\t\t.. note:: The user agent library only contains the most common robot definitions. It is not a complete list of bots.\n\t\t\tThere are hundreds of them so searching for each one would not be very efficient. If you find that some bots\n\t\t\tthat commonly visit your site are missing from the list you can add them to your\n\t\t\t**application/config/user_agents.php** file.\n\n\t.. php:method:: is_referral()\n\n\t\t:returns:\tTRUE if the user agent is a referral, FALSE if not\n\t\t:rtype:\tbool\n\n\t\tReturns TRUE/FALSE (boolean) if the user agent was referred from another site.\n\n\t.. php:method:: browser()\n\n\t\t:returns:\tDetected browser or an empty string\n\t\t:rtype:\tstring\n\n\t\tReturns a string containing the name of the web browser viewing your site.\n\n\t.. php:method:: version()\n\n\t\t:returns:\tDetected browser version or an empty string\n\t\t:rtype:\tstring\n\n\t\tReturns a string containing the version number of the web browser viewing your site.\n\n\t.. php:method:: mobile()\n\n\t\t:returns:\tDetected mobile device brand or an empty string\n\t\t:rtype:\tstring\n\n\t\tReturns a string containing the name of the mobile device viewing your site.\n\n\t.. php:method:: robot()\n\n\t\t:returns:\tDetected robot name or an empty string\n\t\t:rtype:\tstring\n\n\t\tReturns a string containing the name of the robot viewing your site.\n\n\t.. php:method:: platform()\n\n\t\t:returns:\tDetected operating system or an empty string\n\t\t:rtype:\tstring\n\n\t\tReturns a string containing the platform viewing your site (Linux, Windows, OS X, etc.).\n\n\t.. php:method:: referrer()\n\n\t\t:returns:\tDetected referrer or an empty string\n\t\t:rtype:\tstring\n\n\t\tThe referrer, if the user agent was referred from another site. Typically you'll test for this as follows::\n\n\t\t\tif ($this->agent->is_referral())\n\t\t\t{\n\t\t\t\techo $this->agent->referrer();\n\t\t\t}\n\n\t.. php:method:: agent_string()\n\n\t\t:returns:\tFull user agent string or an empty string\n\t\t:rtype:\tstring\n\n\t\tReturns a string containing the full user agent string. Typically it will be something like this::\n\n\t\t\tMozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.0.4) Gecko/20060613 Camino/1.0.2\n\n\t.. php:method:: accept_lang([$lang = 'en'])\n\n\t\t:param\tstring\t$lang: Language key\n\t\t:returns:\tTRUE if provided language is accepted, FALSE if not\n\t\t:rtype:\tbool\n\n\t\tLets you determine if the user agent accepts a particular language. Example::\n\n\t\t\tif ($this->agent->accept_lang('en'))\n\t\t\t{\n\t\t\t\techo 'You accept English!';\n\t\t\t}\n\n\t\t.. note:: This method is not typically very reliable since some\tbrowsers do not provide language info,\n\t\t\tand even among those that do, it is not always accurate.\n\n\t.. php:method:: languages()\n\n\t\t:returns:\tAn array list of accepted languages\n\t\t:rtype:\tarray\n\n\t\tReturns an array of languages supported by the user agent.\n\n\t.. php:method:: accept_charset([$charset = 'utf-8'])\n\n\t\t:param\tstring\t$charset: Character set\n\t\t:returns:\tTRUE if the character set is accepted, FALSE if not\n\t\t:rtype:\tbool\n\n\t\tLets you determine if the user agent accepts a particular character set. Example::\n\n\t\t\tif ($this->agent->accept_charset('utf-8'))\n\t\t\t{\n\t\t\t\techo 'You browser supports UTF-8!';\n\t\t\t}\n\n\t\t.. note:: This method is not typically very reliable since some browsers do not provide character-set info,\n\t\t\tand even among those that do, it is not always accurate.\n\n\t.. php:method:: charsets()\n\n\t\t:returns:\tAn array list of accepted character sets\n\t\t:rtype:\tarray\n\n\t\tReturns an array of character sets accepted by the user agent.\n\n\t.. php:method:: parse($string)\n\n\t\t:param\tstring\t$string: A custom user-agent string\n\t\t:rtype:\tvoid\n\n\t\tParses a custom user-agent string, different from the one reported by the current visitor."
  },
  {
    "path": "user_guide_src/source/libraries/xmlrpc.rst",
    "content": "##################################\nXML-RPC and XML-RPC Server Classes\n##################################\n\nCodeIgniter's XML-RPC classes permit you to send requests to another\nserver, or set up your own XML-RPC server to receive requests.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n****************\nWhat is XML-RPC?\n****************\n\nQuite simply it is a way for two computers to communicate over the\ninternet using XML. One computer, which we will call the client, sends\nan XML-RPC **request** to another computer, which we will call the\nserver. Once the server receives and processes the request it will send\nback a **response** to the client.\n\nFor example, using the MetaWeblog API, an XML-RPC Client (usually a\ndesktop publishing tool) will send a request to an XML-RPC Server\nrunning on your site. This request might be a new weblog entry being\nsent for publication, or it could be a request for an existing entry for\nediting. When the XML-RPC Server receives this request it will examine\nit to determine which class/method should be called to process the\nrequest. Once processed, the server will then send back a response\nmessage.\n\nFor detailed specifications, you can visit the `XML-RPC <http://www.xmlrpc.com/>`_ site.\n\n***********************\nUsing the XML-RPC Class\n***********************\n\nInitializing the Class\n======================\n\nLike most other classes in CodeIgniter, the XML-RPC and XML-RPCS classes\nare initialized in your controller using the $this->load->library\nfunction:\n\nTo load the XML-RPC class you will use::\n\n\t$this->load->library('xmlrpc');\n\nOnce loaded, the xml-rpc library object will be available using:\n$this->xmlrpc\n\nTo load the XML-RPC Server class you will use::\n\n\t$this->load->library('xmlrpc');\n\t$this->load->library('xmlrpcs');\n\nOnce loaded, the xml-rpcs library object will be available using:\n$this->xmlrpcs\n\n.. note:: When using the XML-RPC Server class you must load BOTH the\n\tXML-RPC class and the XML-RPC Server class.\n\nSending XML-RPC Requests\n========================\n\nTo send a request to an XML-RPC server you must specify the following\ninformation:\n\n-  The URL of the server\n-  The method on the server you wish to call\n-  The *request* data (explained below).\n\nHere is a basic example that sends a simple Weblogs.com ping to the\n`Ping-o-Matic <https://pingomatic.com/>`_\n\n::\n\n\t$this->load->library('xmlrpc');\n\n\t$this->xmlrpc->server('http://rpc.pingomatic.com/', 80);\n\t$this->xmlrpc->method('weblogUpdates.ping');\n\n\t$request = array('My Photoblog', 'http://www.my-site.com/photoblog/');\n\t$this->xmlrpc->request($request);\n\n\tif ( ! $this->xmlrpc->send_request())\n\t{\n\t\techo $this->xmlrpc->display_error();\n\t}\n\nExplanation\n-----------\n\nThe above code initializes the XML-RPC class, sets the server URL and\nmethod to be called (weblogUpdates.ping). The request (in this case, the\ntitle and URL of your site) is placed into an array for transportation,\nand compiled using the request() function. Lastly, the full request is\nsent. If the send_request() method returns false we will display the\nerror message sent back from the XML-RPC Server.\n\nAnatomy of a Request\n====================\n\nAn XML-RPC request is simply the data you are sending to the XML-RPC\nserver. Each piece of data in a request is referred to as a request\nparameter. The above example has two parameters: The URL and title of\nyour site. When the XML-RPC server receives your request, it will look\nfor parameters it requires.\n\nRequest parameters must be placed into an array for transportation, and\neach parameter can be one of seven data types (strings, numbers, dates,\netc.). If your parameters are something other than strings you will have\nto include the data type in the request array.\n\nHere is an example of a simple array with three parameters::\n\n\t$request = array('John', 'Doe', 'www.some-site.com');\n\t$this->xmlrpc->request($request);\n\nIf you use data types other than strings, or if you have several\ndifferent data types, you will place each parameter into its own array,\nwith the data type in the second position::\n\n\t$request = array(\n\t\tarray('John', 'string'),\n\t\tarray('Doe', 'string'),\n\t\tarray(FALSE, 'boolean'),\n\t\tarray(12345, 'int')\n\t); \n\t$this->xmlrpc->request($request);\n\nThe `Data Types <#datatypes>`_ section below has a full list of data\ntypes.\n\nCreating an XML-RPC Server\n==========================\n\nAn XML-RPC Server acts as a traffic cop of sorts, waiting for incoming\nrequests and redirecting them to the appropriate functions for\nprocessing.\n\nTo create your own XML-RPC server involves initializing the XML-RPC\nServer class in your controller where you expect the incoming request to\nappear, then setting up an array with mapping instructions so that\nincoming requests can be sent to the appropriate class and method for\nprocessing.\n\nHere is an example to illustrate::\n\n\t$this->load->library('xmlrpc');\n\t$this->load->library('xmlrpcs');\n\n\t$config['functions']['new_post'] = array('function' => 'My_blog.new_entry');\n\t$config['functions']['update_post'] = array('function' => 'My_blog.update_entry');\n\t$config['object'] = $this;\n\n\t$this->xmlrpcs->initialize($config);\n\t$this->xmlrpcs->serve();\n\nThe above example contains an array specifying two method requests that\nthe Server allows. The allowed methods are on the left side of the\narray. When either of those are received, they will be mapped to the\nclass and method on the right.\n\nThe 'object' key is a special key that you pass an instantiated class\nobject with, which is necessary when the method you are mapping to is\nnot part of the CodeIgniter super object.\n\nIn other words, if an XML-RPC Client sends a request for the new_post\nmethod, your server will load the My_blog class and call the new_entry\nfunction. If the request is for the update_post method, your server\nwill load the My_blog class and call the ``update_entry()`` method.\n\nThe function names in the above example are arbitrary. You'll decide\nwhat they should be called on your server, or if you are using\nstandardized APIs, like the Blogger or MetaWeblog API, you'll use their\nfunction names.\n\nThere are two additional configuration keys you may make use of when\ninitializing the server class: debug can be set to TRUE in order to\nenable debugging, and xss_clean may be set to FALSE to prevent sending\ndata through the Security library's ``xss_clean()`` method.\n\nProcessing Server Requests\n==========================\n\nWhen the XML-RPC Server receives a request and loads the class/method\nfor processing, it will pass an object to that method containing the\ndata sent by the client.\n\nUsing the above example, if the new_post method is requested, the\nserver will expect a class to exist with this prototype::\n\n\tclass My_blog extends CI_Controller {\n\n\t\tpublic function new_post($request)\n\t\t{\n\n\t\t}\n\t}\n\nThe $request variable is an object compiled by the Server, which\ncontains the data sent by the XML-RPC Client. Using this object you will\nhave access to the *request parameters* enabling you to process the\nrequest. When you are done you will send a Response back to the Client.\n\nBelow is a real-world example, using the Blogger API. One of the methods\nin the Blogger API is ``getUserInfo()``. Using this method, an XML-RPC\nClient can send the Server a username and password, in return the Server\nsends back information about that particular user (nickname, user ID,\nemail address, etc.). Here is how the processing function might look::\n\n\tclass My_blog extends CI_Controller {\n\n\t\tpublic function getUserInfo($request)\n\t\t{\n\t\t\t$username = 'smitty';\n\t\t\t$password = 'secretsmittypass';\n\n\t\t\t$this->load->library('xmlrpc');\n\n\t\t\t$parameters = $request->output_parameters();\n\n\t\t\tif ($parameters[1] != $username && $parameters[2] != $password)\n\t\t\t{\n\t\t\t\treturn $this->xmlrpc->send_error_message('100', 'Invalid Access');\n\t\t\t}\n\n\t\t\t$response = array(\n\t\t\t\tarray(\n\t\t\t\t\t'nickname'  => array('Smitty', 'string'),\n\t\t\t\t\t'userid'    => array('99', 'string'),\n\t\t\t\t\t'url'       => array('http://yoursite.com', 'string'),\n\t\t\t\t\t'email'     => array('jsmith@yoursite.com', 'string'),\n\t\t\t\t\t'lastname'  => array('Smith', 'string'),\n\t\t\t\t\t'firstname' => array('John', 'string')\n\t\t\t\t),\n\t                         'struct'\n\t\t\t);\n\n\t\t\treturn $this->xmlrpc->send_response($response);\n\t\t}\n\t}\n\nNotes:\n------\n\nThe ``output_parameters()`` method retrieves an indexed array\ncorresponding to the request parameters sent by the client. In the above\nexample, the output parameters will be the username and password.\n\nIf the username and password sent by the client were not valid, and\nerror message is returned using ``send_error_message()``.\n\nIf the operation was successful, the client will be sent back a response\narray containing the user's info.\n\nFormatting a Response\n=====================\n\nSimilar to *Requests*, *Responses* must be formatted as an array.\nHowever, unlike requests, a response is an array **that contains a\nsingle item**. This item can be an array with several additional arrays,\nbut there can be only one primary array index. In other words, the basic\nprototype is this::\n\n\t$response = array('Response data', 'array');\n\nResponses, however, usually contain multiple pieces of information. In\norder to accomplish this we must put the response into its own array so\nthat the primary array continues to contain a single piece of data.\nHere's an example showing how this might be accomplished::\n\n\t$response = array(\n\t\tarray(\n\t\t\t'first_name' => array('John', 'string'),\n\t\t\t'last_name' => array('Doe', 'string'),\n\t\t\t'member_id' => array(123435, 'int'),\n\t\t\t'todo_list' => array(array('clean house', 'call mom', 'water plants'), 'array'),\n\t\t),\n\t\t'struct'\n\t);\n\nNotice that the above array is formatted as a struct. This is the most\ncommon data type for responses.\n\nAs with Requests, a response can be one of the seven data types listed\nin the `Data Types <#datatypes>`_ section.\n\nSending an Error Response\n=========================\n\nIf you need to send the client an error response you will use the\nfollowing::\n\n\treturn $this->xmlrpc->send_error_message('123', 'Requested data not available');\n\nThe first parameter is the error number while the second parameter is\nthe error message.\n\nCreating Your Own Client and Server\n===================================\n\nTo help you understand everything we've covered thus far, let's create a\ncouple controllers that act as XML-RPC Client and Server. You'll use the\nClient to send a request to the Server and receive a response.\n\nThe Client\n----------\n\nUsing a text editor, create a controller called Xmlrpc_client.php. In\nit, place this code and save it to your application/controllers/\nfolder::\n\n\t<?php\n\n\tclass Xmlrpc_client extends CI_Controller {\n\n\t\tpublic function index()\n\t\t{\n\t\t\t$this->load->helper('url');\n\t\t\t$server_url = site_url('xmlrpc_server');\n\n\t\t\t$this->load->library('xmlrpc');\n\n\t\t\t$this->xmlrpc->server($server_url, 80);\n\t\t\t$this->xmlrpc->method('Greetings');\n\n\t\t\t$request = array('How is it going?');\n\t\t\t$this->xmlrpc->request($request);\n\n\t\t\tif ( ! $this->xmlrpc->send_request())\n\t\t\t{\n\t\t\t\techo $this->xmlrpc->display_error();\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\techo '<pre>';\n\t\t\t\tprint_r($this->xmlrpc->display_response());\n\t\t\t\techo '</pre>';\n\t\t\t}\n\t\t}\n\t}\n\t?>\n\n.. note:: In the above code we are using a \"url helper\". You can find more\n\tinformation in the :doc:`Helpers Functions <../general/helpers>` page.\n\nThe Server\n----------\n\nUsing a text editor, create a controller called Xmlrpc_server.php. In\nit, place this code and save it to your application/controllers/\nfolder::\n\n\t<?php\n\n\tclass Xmlrpc_server extends CI_Controller {\n\n\t\tpublic function index()\n\t\t{\n\t\t\t$this->load->library('xmlrpc');\n\t\t\t$this->load->library('xmlrpcs');\n\n\t\t\t$config['functions']['Greetings'] = array('function' => 'Xmlrpc_server.process');\n\n\t\t\t$this->xmlrpcs->initialize($config);\n\t\t\t$this->xmlrpcs->serve();\n\t\t}\n\n\n\t\tpublic function process($request)\n\t\t{\n\t\t\t$parameters = $request->output_parameters();\n\n\t\t\t$response = array(\n\t\t\t\tarray(\n\t\t\t\t\t'you_said'  => $parameters[0],\n\t\t\t\t\t'i_respond' => 'Not bad at all.'\n\t\t\t\t),\n\t\t\t\t'struct'\n\t\t\t);\n\n\t\t\treturn $this->xmlrpc->send_response($response);\n\t\t}\n\t}\n\n\nTry it!\n-------\n\nNow visit the your site using a URL similar to this::\n\n\texample.com/index.php/xmlrpc_client/\n\nYou should now see the message you sent to the server, and its response\nback to you.\n\nThe client you created sends a message (\"How's is going?\") to the\nserver, along with a request for the \"Greetings\" method. The Server\nreceives the request and maps it to the ``process()`` method, where a\nresponse is sent back.\n\nUsing Associative Arrays In a Request Parameter\n===============================================\n\nIf you wish to use an associative array in your method parameters you\nwill need to use a struct datatype::\n\n\t$request = array(\n\t\tarray(\n\t\t\t// Param 0\n\t\t\tarray('name' => 'John'),\n\t\t\t'struct'\n\t\t),\n\t\tarray(\n\t\t\t// Param 1\n\t\t\tarray(\n\t\t\t\t'size' => 'large',\n\t\t\t\t'shape'=>'round'\n\t\t\t),\n\t\t\t'struct'\n\t\t)\n\t);\n\n\t$this->xmlrpc->request($request);\n\nYou can retrieve the associative array when processing the request in\nthe Server.\n\n::\n\n\t$parameters = $request->output_parameters();\n\t$name = $parameters[0]['name'];\n\t$size = $parameters[1]['size'];\n\t$shape = $parameters[1]['shape'];\n\nData Types\n==========\n\nAccording to the `XML-RPC spec <http://www.xmlrpc.com/spec>`_ there are\nseven types of values that you can send via XML-RPC:\n\n-  *int* or *i4*\n-  *boolean*\n-  *string*\n-  *double*\n-  *dateTime.iso8601*\n-  *base64*\n-  *struct* (contains array of values)\n-  *array* (contains array of values)\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Xmlrpc\n\n\t.. php:method:: initialize([$config = array()])\n\n\t\t:param\tarray\t$config: Configuration data\n\t\t:rtype:\tvoid\n\n\t\tInitializes the XML-RPC library. Accepts an associative array containing your settings.\n\n\t.. php:method:: server($url[, $port = 80[, $proxy = FALSE[, $proxy_port = 8080]]])\n\n\t\t:param\tstring\t$url: XML-RPC server URL\n\t\t:param\tint\t$port: Server port\n\t\t:param\tstring\t$proxy: Optional proxy\n\t\t:param\tint\t$proxy_port: Proxy listening port\n\t\t:rtype:\tvoid\n\n\t\tSets the URL and port number of the server to which a request is to be sent::\n\n\t\t\t$this->xmlrpc->server('http://www.sometimes.com/pings.php', 80);\n\n\t\tBasic HTTP authentication is also supported, simply add it to the server URL::\n\n\t\t\t$this->xmlrpc->server('http://user:pass@localhost/', 80);\n\n\t.. php:method:: timeout($seconds = 5)\n\n\t\t:param\tint\t$seconds: Timeout in seconds\n\t\t:rtype:\tvoid\n\n\t\tSet a time out period (in seconds) after which the request will be canceled::\n\n\t\t\t$this->xmlrpc->timeout(6);\n\n\t\tThis timeout period will be used both for an initial connection to \n                the remote server, as well as for getting a response from it.\n                Make sure you set the timeout before calling ``send_request()``.\n\n\t.. php:method:: method($function)\n\n\t\t:param\tstring\t$function: Method name\n\t\t:rtype:\tvoid\n\n\t\tSets the method that will be requested from the XML-RPC server::\n\n\t\t\t$this->xmlrpc->method('method');\n\n\t\tWhere method is the name of the method.\n\n\t.. php:method:: request($incoming)\n\n\t\t:param\tarray\t$incoming: Request data\n\t\t:rtype:\tvoid\n\n\t\tTakes an array of data and builds request to be sent to XML-RPC server::\n\n\t\t\t$request = array(array('My Photoblog', 'string'), 'http://www.yoursite.com/photoblog/');\n\t\t\t$this->xmlrpc->request($request);\n\n\t.. php:method:: send_request()\n\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tThe request sending method. Returns boolean TRUE or FALSE based on success for failure, enabling it to be used conditionally.\n\n\t.. method set_debug($flag = TRUE)\n\n\t\t:param\tbool\t$flag: Debug status flag\n\t\t:rtype:\tvoid\n\n\t\tEnables or disables debugging, which will display a variety of information and error data helpful during development.\n\n\t.. php:method:: display_error()\n\n\t\t:returns:\tError message string\n\t\t:rtype:\tstring\n\n\t\tReturns an error message as a string if your request failed for some reason.\n\t\t::\n\n\t\t\techo $this->xmlrpc->display_error();\n\n\t.. php:method:: display_response()\n\n\t\t:returns:\tResponse\n\t\t:rtype:\tmixed\n\n\t\tReturns the response from the remote server once request is received. The response will typically be an associative array.\n\t\t::\n\n\t\t\t$this->xmlrpc->display_response();\n\n\t.. php:method:: send_error_message($number, $message)\n\n\t\t:param\tint\t$number: Error number\n\t\t:param\tstring\t$message: Error message\n\t\t:returns:\tXML_RPC_Response instance\n\t\t:rtype:\tXML_RPC_Response\n\n\t\tThis method lets you send an error message from your server to the client.\n\t\tFirst parameter is the error number while the second parameter is the error message.\n\t\t::\n\n\t\t\treturn $this->xmlrpc->send_error_message(123, 'Requested data not available');\n\n\t.. method send_response($response)\n\n\t\t:param\tarray\t$response: Response data\n\t\t:returns:\tXML_RPC_Response instance\n\t\t:rtype:\tXML_RPC_Response\n\n\t\tLets you send the response from your server to the client. An array of valid data values must be sent with this method.\n\t\t::\n\n\t\t\t$response = array(\n\t\t\t\tarray(\n\t\t\t\t\t'flerror' => array(FALSE, 'boolean'),\n\t\t\t\t\t'message' => \"Thanks for the ping!\"\n\t\t\t\t),\n\t\t\t\t'struct'\n\t\t\t);\n\n\t\t\treturn $this->xmlrpc->send_response($response);\n"
  },
  {
    "path": "user_guide_src/source/libraries/zip.rst",
    "content": "##################\nZip Encoding Class\n##################\n\nCodeIgniter's Zip Encoding Class permits you to create Zip archives.\nArchives can be downloaded to your desktop or saved to a directory.\n\n.. contents::\n  :local:\n\n.. raw:: html\n\n  <div class=\"custom-index container\"></div>\n\n****************************\nUsing the Zip Encoding Class\n****************************\n\nInitializing the Class\n======================\n\nLike most other classes in CodeIgniter, the Zip class is initialized in\nyour controller using the $this->load->library function::\n\n\t$this->load->library('zip');\n\nOnce loaded, the Zip library object will be available using::\n\n\t$this->zip\n\nUsage Example\n=============\n\nThis example demonstrates how to compress a file, save it to a folder on\nyour server, and download it to your desktop.\n\n::\n\n\t$name = 'mydata1.txt';\n\t$data = 'A Data String!';\n\n\t$this->zip->add_data($name, $data);\n\n\t// Write the zip file to a folder on your server. Name it \"my_backup.zip\"\n\t$this->zip->archive('/path/to/directory/my_backup.zip');\n\n\t// Download the file to your desktop. Name it \"my_backup.zip\"\n\t$this->zip->download('my_backup.zip');\n\n***************\nClass Reference\n***************\n\n.. php:class:: CI_Zip\n\n\t.. attribute:: $compression_level = 2\n\n\t\tThe compression level to use.\n\n\t\tIt can range from 0 to 9, with 9 being the highest and 0 effectively disabling compression::\n\n\t\t\t$this->zip->compression_level = 0;\n\n\t.. php:method:: add_data($filepath[, $data = NULL])\n\n\t\t:param\tmixed\t$filepath: A single file path or an array of file => data pairs\n\t\t:param\tarray\t$data: File contents (ignored if $filepath is an array)\n\t\t:rtype:\tvoid\n\n\t\tAdds data to the Zip archive. Can work both in single and multiple files mode.\n\n\t\tWhen adding a single file, the first parameter must contain the name you would\n\t\tlike given to the file and the second must contain the file contents::\n\n\t\t\t$name = 'mydata1.txt';\n\t\t\t$data = 'A Data String!';\n\t\t\t$this->zip->add_data($name, $data);\n\n\t\t\t$name = 'mydata2.txt';\n\t\t\t$data = 'Another Data String!';\n\t\t\t$this->zip->add_data($name, $data);\n\n\t\tWhen adding multiple files, the first parameter must contain *file => contents* pairs\n\t\tand the second parameter is ignored::\n\n\t\t\t$data = array(\n\t\t\t\t'mydata1.txt' => 'A Data String!',\n\t\t\t\t'mydata2.txt' => 'Another Data String!'\n\t\t\t);\n\n\t\t\t$this->zip->add_data($data);\n\n\t\tIf you would like your compressed data organized into sub-directories, simply include\n\t\tthe path as part of the filename(s)::\n\n\t\t\t$name = 'personal/my_bio.txt';\n\t\t\t$data = 'I was born in an elevator...';\n\n\t\t\t$this->zip->add_data($name, $data);\n\n\t\tThe above example will place my_bio.txt inside a folder called personal.\n\n\t.. php:method:: add_dir($directory)\n\n\t\t:param\tmixed\t$directory: Directory name string or an array of multiple directories\n\t\t:rtype:\tvoid\n\n\t\tPermits you to add a directory. Usually this method is unnecessary since you can place\n\t\tyour data into directories when using ``$this->zip->add_data()``, but if you would like\n\t\tto create an empty directory you can do so::\n\n\t\t\t$this->zip->add_dir('myfolder'); // Creates a directory called \"myfolder\"\n\n\t.. php:method:: read_file($path[, $archive_filepath = FALSE])\n\n\t\t:param\tstring\t$path: Path to file\n\t\t:param\tmixed\t$archive_filepath: New file name/path (string) or (boolean) whether to maintain the original filepath\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tPermits you to compress a file that already exists somewhere on your server.\n\t\tSupply a file path and the zip class will read it and add it to the archive::\n\n\t\t\t$path = '/path/to/photo.jpg';\n\n\t\t\t$this->zip->read_file($path);\n\n\t\t\t// Download the file to your desktop. Name it \"my_backup.zip\"\n\t\t\t$this->zip->download('my_backup.zip');\n\n\t\tIf you would like the Zip archive to maintain the directory structure of\n\t\tthe file in it, pass TRUE (boolean) in the second parameter. Example::\n\n\t\t\t$path = '/path/to/photo.jpg';\n\n\t\t\t$this->zip->read_file($path, TRUE);\n\n\t\t\t// Download the file to your desktop. Name it \"my_backup.zip\"\n\t\t\t$this->zip->download('my_backup.zip');\n\n\t\tIn the above example, photo.jpg will be placed into the *path/to/* directory.\n\n\t\tYou can also specify a new name (path included) for the added file on the fly::\n\n\t\t\t$path = '/path/to/photo.jpg';\n\t\t\t$new_path = '/new/path/some_photo.jpg';\n\n\t\t\t$this->zip->read_file($path, $new_path);\n\n\t\t\t// Download ZIP archive containing /new/path/some_photo.jpg\n\t\t\t$this->zip->download('my_archive.zip');\n\n\t.. php:method:: read_dir($path[, $preserve_filepath = TRUE[, $root_path = NULL]])\n\n\t\t:param\tstring\t$path: Path to directory\n\t\t:param\tbool\t$preserve_filepath: Whether to maintain the original path\n\t\t:param\tstring\t$root_path: Part of the path to exclude from the archive directory\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tPermits you to compress a directory (and its contents) that already exists somewhere on your server.\n\t\tSupply a path to the directory and the zip class will recursively read and recreate it as a Zip archive.\n\t\tAll files contained within the supplied path will be encoded, as will any sub-directories contained within it. Example::\n\n\t\t\t$path = '/path/to/your/directory/';\n\n\t\t\t$this->zip->read_dir($path);\n\n\t\t\t// Download the file to your desktop. Name it \"my_backup.zip\"\n\t\t\t$this->zip->download('my_backup.zip');\n\n\t\tBy default the Zip archive will place all directories listed in the first parameter\n\t\tinside the zip. If you want the tree preceding the target directory to be ignored,\n\t\tyou can pass FALSE (boolean) in the second parameter. Example::\n\n\t\t\t$path = '/path/to/your/directory/';\n\n\t\t\t$this->zip->read_dir($path, FALSE);\n\n\t\tThis will create a ZIP with a directory named \"directory\" inside, then all sub-directories\n\t\tstored correctly inside that, but will not include the */path/to/your* part of the path.\n\n\t.. php:method:: archive($filepath)\n\n\t\t:param\tstring\t$filepath: Path to target zip archive\n\t\t:returns:\tTRUE on success, FALSE on failure\n\t\t:rtype:\tbool\n\n\t\tWrites the Zip-encoded file to a directory on your server. Submit a valid server path\n\t\tending in the file name. Make sure the directory is writable (755 is usually OK).\n\t\tExample::\n\n\t\t\t$this->zip->archive('/path/to/folder/myarchive.zip'); // Creates a file named myarchive.zip\n\n\t.. php:method:: download($filename = 'backup.zip')\n\n\t\t:param\tstring\t$filename: Archive file name\n\t\t:rtype:\tvoid\n\n\t\tCauses the Zip file to be downloaded from your server.\n\t\tYou must pass the name you would like the zip file called. Example::\n\n\t\t\t$this->zip->download('latest_stuff.zip'); // File will be named \"latest_stuff.zip\"\n\n\t\t.. note:: Do not display any data in the controller in which you call\n\t\t\tthis method since it sends various server headers that cause the\n\t\t\tdownload to happen and the file to be treated as binary.\n\n\t.. php:method:: get_zip()\n\n\t\t:returns:\tZip file content\n\t\t:rtype:\tstring\n\n\t\tReturns the Zip-compressed file data. Generally you will not need this method unless you\n\t\twant to do something unique with the data. Example::\n\n\t\t\t$name = 'my_bio.txt';\n\t\t\t$data = 'I was born in an elevator...';\n\n\t\t\t$this->zip->add_data($name, $data);\n\n\t\t\t$zip_file = $this->zip->get_zip();\n\n\t.. php:method:: clear_data()\n\n\t\t:rtype:\tvoid\n\n\t\tThe Zip class caches your zip data so that it doesn't need to recompile the Zip archive\n\t\tfor each method you use above. If, however, you need to create multiple Zip archives,\n\t\teach with different data, you can clear the cache between calls. Example::\n\n\t\t\t$name = 'my_bio.txt';\n\t\t\t$data = 'I was born in an elevator...';\n\n\t\t\t$this->zip->add_data($name, $data);\n\t\t\t$zip_file = $this->zip->get_zip();\n\n\t\t\t$this->zip->clear_data();\n\n\t\t\t$name = 'photo.jpg';\n\t\t\t$this->zip->read_file(\"/path/to/photo.jpg\"); // Read the file's contents\n\n\t\t\t$this->zip->download('myphotos.zip');\n"
  },
  {
    "path": "user_guide_src/source/license.rst",
    "content": "#####################\nThe MIT License (MIT)\n#####################\n\nCopyright (c) 2019 - 2022, CodeIgniter Foundation\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "user_guide_src/source/overview/appflow.rst",
    "content": "######################\nApplication Flow Chart\n######################\n\nThe following graphic illustrates how data flows throughout the system:\n\n|CodeIgniter application flow|\n\n#. The index.php serves as the front controller, initializing the base\n   resources needed to run CodeIgniter.\n#. The Router examines the HTTP request to determine what should be done\n   with it.\n#. If a cache file exists, it is sent directly to the browser, bypassing\n   the normal system execution.\n#. Security. Before the application controller is loaded, the HTTP\n   request and any user submitted data is filtered for security.\n#. The Controller loads the model, core libraries, helpers, and any\n   other resources needed to process the specific request.\n#. The finalized View is rendered then sent to the web browser to be\n   seen. If caching is enabled, the view is cached first so that on\n   subsequent requests it can be served.\n\n.. |CodeIgniter application flow| image:: ../images/appflowchart.gif\n"
  },
  {
    "path": "user_guide_src/source/overview/at_a_glance.rst",
    "content": "#######################\nCodeIgniter at a Glance\n#######################\n\nCodeIgniter is an Application Framework\n=======================================\n\nCodeIgniter is a toolkit for people who build web applications using\nPHP. Its goal is to enable you to develop projects much faster than you\ncould if you were writing code from scratch, by providing a rich set of\nlibraries for commonly needed tasks, as well as a simple interface and\nlogical structure to access these libraries. CodeIgniter lets you\ncreatively focus on your project by minimizing the amount of code needed\nfor a given task.\n\nCodeIgniter is Free\n===================\n\nCodeIgniter is licensed under the MIT license so you can use it however\nyou please. For more information please read the\n:doc:`license agreement <../license>`.\n\nCodeIgniter is Light Weight\n===========================\n\nTruly light weight. The core system requires only a few very small\nlibraries. This is in stark contrast to many frameworks that require\nsignificantly more resources. Additional libraries are loaded\ndynamically upon request, based on your needs for a given process, so\nthe base system is very lean and quite fast.\n\nCodeIgniter is Fast\n===================\n\nReally fast. We challenge you to find a framework that has better\nperformance than CodeIgniter.\n\nCodeIgniter Uses M-V-C\n======================\n\nCodeIgniter uses the Model-View-Controller approach, which allows great\nseparation between logic and presentation. This is particularly good for\nprojects in which designers are working with your template files, as the\ncode these files contain will be minimized. We describe MVC in more\ndetail on its own page.\n\nCodeIgniter Generates Clean URLs\n================================\n\nThe URLs generated by CodeIgniter are clean and search-engine friendly.\nRather than using the standard \"query string\" approach to URLs that is\nsynonymous with dynamic systems, CodeIgniter uses a segment-based\napproach::\n\n\texample.com/news/article/345\n\n.. note:: By default the *index.php* file is included in the URL but it can\n\tbe removed using a simple *.htaccess* file.\n\nCodeIgniter Packs a Punch\n=========================\n\nCodeIgniter comes with full-range of libraries that enable the most\ncommonly needed web development tasks, like accessing a database,\nsending email, validating form data, maintaining sessions, manipulating\nimages, working with XML-RPC data and much more.\n\nCodeIgniter is Extensible\n=========================\n\nThe system can be easily extended through the use of your own libraries,\nhelpers, or through class extensions or system hooks.\n\nCodeIgniter Does Not Require a Template Engine\n==============================================\n\nAlthough CodeIgniter *does* come with a simple template parser that can\nbe optionally used, it does not force you to use one. Template engines\nsimply can not match the performance of native PHP, and the syntax that\nmust be learned to use a template engine is usually only marginally\neasier than learning the basics of PHP. Consider this block of PHP code::\n\n\t<ul>\n\t<?php foreach ($addressbook as $name):?>\n\t\t<li><?=$name?></li>\n\t<?php endforeach; ?>\n\t</ul>\n\nContrast this with the pseudo-code used by a template engine::\n\n\t<ul>\n\t{foreach from=$addressbook item=\"name\"}\n\t\t<li>{$name}</li>\n\t{/foreach}\n\t</ul>\n\nYes, the template engine example is a bit cleaner, but it comes at the\nprice of performance, as the pseudo-code must be converted back into PHP\nto run. Since one of our goals is *maximum performance*, we opted to not\nrequire the use of a template engine.\n\nCodeIgniter is Thoroughly Documented\n====================================\n\nProgrammers love to code and hate to write documentation. We're no\ndifferent, of course, but since documentation is **as important** as the\ncode itself, we are committed to doing it. Our source code is extremely\nclean and well commented as well.\n\nCodeIgniter has a Friendly Community of Users\n=============================================\n\nOur growing community of users can be seen actively participating in our\n`Community Forums <https://forum.codeigniter.com/>`_.\n"
  },
  {
    "path": "user_guide_src/source/overview/features.rst",
    "content": "####################\nCodeIgniter Features\n####################\n\nFeatures in and of themselves are a very poor way to judge an\napplication since they tell you nothing about the user experience, or\nhow intuitively or intelligently it is designed. Features don't reveal\nanything about the quality of the code, or the performance, or the\nattention to detail, or security practices. The only way to really judge\nan app is to try it and get to know the code.\n:doc:`Installing <../installation/index>` CodeIgniter is child's play so\nwe encourage you to do just that. In the mean time here's a list of\nCodeIgniter's main features.\n\n-  Model-View-Controller Based System\n-  Extremely Light Weight\n-  Full Featured database classes with support for several platforms.\n-  Query Builder Database Support\n-  Form and Data Validation\n-  Security and XSS Filtering\n-  Session Management\n-  Email Sending Class. Supports Attachments, HTML/Text email, multiple\n   protocols (sendmail, SMTP, and Mail) and more.\n-  Image Manipulation Library (cropping, resizing, rotating, etc.).\n   Supports GD, ImageMagick, and NetPBM\n-  File Uploading Class\n-  FTP Class\n-  Localization\n-  Pagination\n-  Data Encryption\n-  Benchmarking\n-  Full Page Caching\n-  Error Logging\n-  Application Profiling\n-  Calendaring Class\n-  User Agent Class\n-  Zip Encoding Class\n-  Template Engine Class\n-  Trackback Class\n-  XML-RPC Library\n-  Unit Testing Class\n-  Search-engine Friendly URLs\n-  Flexible URI Routing\n-  Support for Hooks and Class Extensions\n-  Large library of \"helper\" functions\n\n"
  },
  {
    "path": "user_guide_src/source/overview/getting_started.rst",
    "content": "################################\nGetting Started With CodeIgniter\n################################\n\nAny software application requires some effort to learn. We've done our\nbest to minimize the learning curve while making the process as\nenjoyable as possible.\n\nThe first step is to :doc:`install <../installation/index>`\nCodeIgniter, then read all the topics in the **Introduction** section of\nthe Table of Contents.\n\nNext, read each of the **General Topics** pages in order. Each topic\nbuilds on the previous one, and includes code examples that you are\nencouraged to try.\n\nOnce you understand the basics you'll be ready to explore the **Class\nReference** and **Helper Reference** pages to learn to utilize the\nnative libraries and helper files.\n\nFeel free to take advantage of our `Community\nForums <https://forum.codeigniter.com/>`_ if you have questions or\nproblems, and our `Wiki <https://github.com/bcit-ci/CodeIgniter/wiki>`_ to see code\nexamples posted by other users.\n"
  },
  {
    "path": "user_guide_src/source/overview/goals.rst",
    "content": "##############################\nDesign and Architectural Goals\n##############################\n\nOur goal for CodeIgniter is maximum performance, capability, and\nflexibility in the smallest, lightest possible package.\n\nTo meet this goal we are committed to benchmarking, re-factoring, and\nsimplifying at every step of the development process, rejecting anything\nthat doesn't further the stated objective.\n\nFrom a technical and architectural standpoint, CodeIgniter was created\nwith the following objectives:\n\n-  **Dynamic Instantiation.** In CodeIgniter, components are loaded and\n   routines executed only when requested, rather than globally. No\n   assumptions are made by the system regarding what may be needed\n   beyond the minimal core resources, so the system is very light-weight\n   by default. The events, as triggered by the HTTP request, and the\n   controllers and views you design will determine what is invoked.\n-  **Loose Coupling.** Coupling is the degree to which components of a\n   system rely on each other. The less components depend on each other\n   the more reusable and flexible the system becomes. Our goal was a\n   very loosely coupled system.\n-  **Component Singularity.** Singularity is the degree to which\n   components have a narrowly focused purpose. In CodeIgniter, each\n   class and its functions are highly autonomous in order to allow\n   maximum usefulness.\n\nCodeIgniter is a dynamically instantiated, loosely coupled system with\nhigh component singularity. It strives for simplicity, flexibility, and\nhigh performance in a small footprint package.\n"
  },
  {
    "path": "user_guide_src/source/overview/index.rst",
    "content": "####################\nCodeIgniter Overview\n####################\n\nThe following pages describe the broad concepts behind CodeIgniter:\n\n.. toctree::\n\t:titlesonly:\n\t\n\tGetting Started <getting_started>\n\tCodeIgniter at a Glance <at_a_glance>\n\tSupported Features <features>\n\tApplication Flow Chart <appflow>\n\tModel-View-Controller <mvc>\n\tArchitectural Goals <goals>"
  },
  {
    "path": "user_guide_src/source/overview/mvc.rst",
    "content": "#####################\nModel-View-Controller\n#####################\n\nCodeIgniter is based on the Model-View-Controller development pattern.\nMVC is a software approach that separates application logic from\npresentation. In practice, it permits your web pages to contain minimal\nscripting since the presentation is separate from the PHP scripting.\n\n-  The **Model** represents your data structures. Typically your model\n   classes will contain functions that help you retrieve, insert, and\n   update information in your database.\n-  The **View** is the information that is being presented to a user. A\n   View will normally be a web page, but in CodeIgniter, a view can also\n   be a page fragment like a header or footer. It can also be an RSS\n   page, or any other type of \"page\".\n-  The **Controller** serves as an *intermediary* between the Model, the\n   View, and any other resources needed to process the HTTP request and\n   generate a web page.\n\nCodeIgniter has a fairly loose approach to MVC since Models are not\nrequired. If you don't need the added separation, or find that\nmaintaining models requires more complexity than you want, you can\nignore them and build your application minimally using Controllers and\nViews. CodeIgniter also enables you to incorporate your own existing\nscripts, or even develop core libraries for the system, enabling you to\nwork in a way that makes the most sense to you.\n"
  },
  {
    "path": "user_guide_src/source/tutorial/conclusion.rst",
    "content": "##########\nConclusion\n##########\n\nThis tutorial did not cover all of the things you might expect of a\nfull-fledged content management system, but it introduced you to the\nmore important topics of routing, writing controllers, and models. We\nhope this tutorial gave you an insight into some of CodeIgniter's basic\ndesign patterns, which you can expand upon.\n\nNow that you've completed this tutorial, we recommend you check out the\nrest of the documentation. CodeIgniter is often praised because of its\ncomprehensive documentation. Use this to your advantage and read the\n\"Introduction\" and \"General Topics\" sections thoroughly. You should read\nthe class and helper references when needed.\n\nEvery intermediate PHP programmer should be able to get the hang of\nCodeIgniter within a few days.\n\nIf you still have questions about the framework or your own CodeIgniter\ncode, you can:\n\n-  Check out our `forums <https://forum.codeigniter.com/>`_\n-  Visit our `IRC chatroom <https://github.com/bcit-ci/CodeIgniter/wiki/IRC>`_\n-  Explore the `Wiki <https://github.com/bcit-ci/CodeIgniter/wiki/>`_\n\n"
  },
  {
    "path": "user_guide_src/source/tutorial/create_news_items.rst",
    "content": "#################\nCreate news items\n#################\n\nYou now know how you can read data from a database using CodeIgniter, but\nyou haven't written any information to the database yet. In this section\nyou'll expand your news controller and model created earlier to include\nthis functionality.\n\nCreate a form\n-------------\n\nTo input data into the database you need to create a form where you can\ninput the information to be stored. This means you'll be needing a form\nwith two fields, one for the title and one for the text. You'll derive\nthe slug from our title in the model. Create the new view at\n*application/views/news/create.php*.\n\n::\n\n    <h2><?php echo $title; ?></h2>\n\n    <?php echo validation_errors(); ?>\n\n    <?php echo form_open('news/create'); ?>\n\n        <label for=\"title\">Title</label> \n        <input type=\"text\" name=\"title\" /><br />\n\n        <label for=\"text\">Text</label>\n        <textarea name=\"text\"></textarea><br />\n\n        <input type=\"submit\" name=\"submit\" value=\"Create news item\" /> \n\n    </form>\n\nThere are only two things here that probably look unfamiliar to you: the\n``form_open()`` function and the ``validation_errors()`` function.\n\nThe first function is provided by the :doc:`form\nhelper <../helpers/form_helper>` and renders the form element and\nadds extra functionality, like adding a hidden :doc:`CSRF prevention\nfield <../libraries/security>`. The latter is used to report\nerrors related to form validation.\n\nGo back to your news controller. You're going to do two things here,\ncheck whether the form was submitted and whether the submitted data\npassed the validation rules. You'll use the :doc:`form\nvalidation <../libraries/form_validation>` library to do this.\n\n::\n\n    public function create()\n    {\n        $this->load->helper('form');\n        $this->load->library('form_validation');\n        \n        $data['title'] = 'Create a news item';\n        \n        $this->form_validation->set_rules('title', 'Title', 'required');\n        $this->form_validation->set_rules('text', 'Text', 'required');\n        \n        if ($this->form_validation->run() === FALSE)\n        {\n            $this->load->view('templates/header', $data);   \n            $this->load->view('news/create');\n            $this->load->view('templates/footer');\n            \n        }\n        else\n        {\n            $this->news_model->set_news();\n            $this->load->view('news/success');\n        }\n    }\n\nThe code above adds a lot of functionality. The first few lines load the\nform helper and the form validation library. After that, rules for the\nform validation are set. The ``set_rules()`` method takes three arguments;\nthe name of the input field, the name to be used in error messages, and\nthe rule. In this case the title and text fields are required.\n\nCodeIgniter has a powerful form validation library as demonstrated\nabove. You can read :doc:`more about this library\nhere <../libraries/form_validation>`.\n\nContinuing down, you can see a condition that checks whether the form\nvalidation ran successfully. If it did not, the form is displayed, if it\nwas submitted **and** passed all the rules, the model is called. After\nthis, a view is loaded to display a success message. Create a view at\n*application/views/news/success.php* and write a success message.\n\nModel\n-----\n\nThe only thing that remains is writing a method that writes the data to\nthe database. You'll use the Query Builder class to insert the\ninformation and use the input library to get the posted data. Open up\nthe model created earlier and add the following:\n\n::\n\n    public function set_news()\n    {\n        $this->load->helper('url');\n        \n        $slug = url_title($this->input->post('title'), 'dash', TRUE);\n        \n        $data = array(\n            'title' => $this->input->post('title'),\n            'slug' => $slug,\n            'text' => $this->input->post('text')\n        );\n        \n        return $this->db->insert('news', $data);\n    }\n\nThis new method takes care of inserting the news item into the database.\nThe third line contains a new function, url\\_title(). This function -\nprovided by the :doc:`URL helper <../helpers/url_helper>` - strips down\nthe string you pass it, replacing all spaces by dashes (-) and makes\nsure everything is in lowercase characters. This leaves you with a nice\nslug, perfect for creating URIs.\n\nLet's continue with preparing the record that is going to be inserted\nlater, inside the ``$data`` array. Each element corresponds with a column in\nthe database table created earlier. You might notice a new method here,\nnamely the ``post()`` method from the :doc:`input\nlibrary <../libraries/input>`. This method makes sure the data is\nsanitized, protecting you from nasty attacks from others. The input\nlibrary is loaded by default. At last, you insert our ``$data`` array into\nour database.\n\nRouting\n-------\n\nBefore you can start adding news items into your CodeIgniter application\nyou have to add an extra rule to *config/routes.php* file. Make sure your\nfile contains the following. This makes sure CodeIgniter sees 'create'\nas a method instead of a news item's slug.\n\n::\n\n    $route['news/create'] = 'news/create';\n    $route['news/(:any)'] = 'news/view/$1';\n    $route['news'] = 'news';\n    $route['(:any)'] = 'pages/view/$1';\n    $route['default_controller'] = 'pages/view';\n\nNow point your browser to your local development environment where you\ninstalled CodeIgniter and add index.php/news/create to the URL.\nCongratulations, you just created your first CodeIgniter application!\nAdd some news and check out the different pages you made.\n"
  },
  {
    "path": "user_guide_src/source/tutorial/index.rst",
    "content": "########\nTutorial\n########\n\nThis tutorial is intended to introduce you to the CodeIgniter framework\nand the basic principles of MVC architecture. It will show you how a\nbasic CodeIgniter application is constructed in step-by-step fashion.\n\nIn this tutorial, you will be creating a **basic news application**. You\nwill begin by writing the code that can load static pages. Next, you\nwill create a news section that reads news items from a database.\nFinally, you'll add a form to create news items in the database.\n\nThis tutorial will primarily focus on:\n\n-  Model-View-Controller basics\n-  Routing basics\n-  Form validation\n-  Performing basic database queries using \"Query Builder\"\n\nThe entire tutorial is split up over several pages, each explaining a\nsmall part of the functionality of the CodeIgniter framework. You'll go\nthrough the following pages:\n\n-  Introduction, this page, which gives you an overview of what to\n   expect.\n-  :doc:`Static pages <static_pages>`, which will teach you the basics\n   of controllers, views and routing.\n-  :doc:`News section <news_section>`, where you'll start using models\n   and will be doing some basic database operations.\n-  :doc:`Create news items <create_news_items>`, which will introduce\n   more advanced database operations and form validation.\n-  :doc:`Conclusion <conclusion>`, which will give you some pointers on\n   further reading and other resources.\n\nEnjoy your exploration of the CodeIgniter framework.\n\n.. toctree::\n\t:glob:\n\t:hidden:\n\t:titlesonly:\n\t\n\tstatic_pages\n\tnews_section\n\tcreate_news_items\n\tconclusion"
  },
  {
    "path": "user_guide_src/source/tutorial/news_section.rst",
    "content": "############\nNews section\n############\n\nIn the last section, we went over some basic concepts of the framework\nby writing a class that includes static pages. We cleaned up the URI by\nadding custom routing rules. Now it's time to introduce dynamic content\nand start using a database.\n\nSetting up your model\n---------------------\n\nInstead of writing database operations right in the controller, queries\nshould be placed in a model, so they can easily be reused later. Models\nare the place where you retrieve, insert, and update information in your\ndatabase or other data stores. They represent your data.\n\nOpen up the *application/models/* directory and create a new file called\n*News_model.php* and add the following code. Make sure you've configured\nyour database properly as described :doc:`here <../database/configuration>`.\n\n::\n\n\t<?php\n\tclass News_model extends CI_Model {\n\n\t\tpublic function __construct()\n\t\t{\n\t\t\t$this->load->database();\n\t\t}\n\t}\n\nThis code looks similar to the controller code that was used earlier. It\ncreates a new model by extending ``CI_Model`` and loads the database\nlibrary. This will make the database class available through the\n``$this->db`` object.\n\nBefore querying the database, a database schema has to be created.\nConnect to your database and run the SQL command below (MySQL).\nAlso add some seed records.\n\n::\n\n\tCREATE TABLE news (\n\t\tid int(11) NOT NULL AUTO_INCREMENT,\n\t\ttitle varchar(128) NOT NULL,\n\t\tslug varchar(128) NOT NULL,\n\t\ttext text NOT NULL,\n\t\tPRIMARY KEY (id),\n\t\tKEY slug (slug)\n\t);\n\nNow that the database and a model have been set up, you'll need a method\nto get all of our posts from our database. To do this, the database\nabstraction layer that is included with CodeIgniter — \n:doc:`Query Builder <../database/query_builder>` — is used. This makes it\npossible to write your 'queries' once and make them work on :doc:`all\nsupported database systems <../general/requirements>`. Add the\nfollowing code to your model.\n\n::\n\n\tpublic function get_news($slug = FALSE)\n\t{\n\t\tif ($slug === FALSE)\n\t\t{\n\t\t\t$query = $this->db->get('news');\n\t\t\treturn $query->result_array();\n\t\t}\n\n\t\t$query = $this->db->get_where('news', array('slug' => $slug));\n\t\treturn $query->row_array();\n\t}\n\nWith this code you can perform two different queries. You can get all\nnews records, or get a news item by its `slug <#>`_. You might have\nnoticed that the ``$slug`` variable wasn't sanitized before running the\nquery; :doc:`Query Builder <../database/query_builder>` does this for you.\n\nDisplay the news\n----------------\n\nNow that the queries are written, the model should be tied to the views\nthat are going to display the news items to the user. This could be done\nin our ``Pages`` controller created earlier, but for the sake of clarity,\na new ``News`` controller is defined. Create the new controller at\n*application/controllers/News.php*.\n\n::\n\n\t<?php\n\tclass News extends CI_Controller {\n\n\t\tpublic function __construct()\n\t\t{\n\t\t\tparent::__construct();\n\t\t\t$this->load->model('news_model');\n\t\t\t$this->load->helper('url_helper');\n\t\t}\n\n\t\tpublic function index()\n\t\t{\n\t\t\t$data['news'] = $this->news_model->get_news();\n\t\t}\n\n\t\tpublic function view($slug = NULL)\n\t\t{\n\t\t\t$data['news_item'] = $this->news_model->get_news($slug);\n\t\t}\n\t}\n\nLooking at the code, you may see some similarity with the files we\ncreated earlier. First, the ``__construct()`` method: it calls the\nconstructor of its parent class (``CI_Controller``) and loads the model,\nso it can be used in all other methods in this controller.\nIt also loads a collection of :doc:`URL Helper <../helpers/url_helper>`\nfunctions, because we'll use one of them in a view later.\n\nNext, there are two methods to view news items: one for all news items and one for a specific\nnews item. You can see that the ``$slug`` variable is passed to the model's\nmethod in the second method. The model is using this slug to identify the\nnews item to be returned.\n\nNow the data is retrieved by the controller through our model, but\nnothing is displayed yet. The next thing to do is passing this data to\nthe views.\n\n::\n\n\tpublic function index()\n\t{\n\t\t$data['news'] = $this->news_model->get_news();\n\t\t$data['title'] = 'News archive';\n\n\t\t$this->load->view('templates/header', $data);\n\t\t$this->load->view('news/index', $data);\n\t\t$this->load->view('templates/footer');\n\t}\n\nThe code above gets all news records from the model and assigns it to a\nvariable. The value for the title is also assigned to the ``$data['title']``\nelement and all data is passed to the views. You now need to create a\nview to render the news items. Create *application/views/news/index.php*\nand add the next piece of code.\n\n::\n\n\t<h2><?php echo $title; ?></h2>\n\t\n\t<?php foreach ($news as $news_item): ?>\n\n\t\t<h3><?php echo $news_item['title']; ?></h3>\n\t\t<div class=\"main\">\n\t\t\t<?php echo $news_item['text']; ?>\n\t\t</div>\n\t\t<p><a href=\"<?php echo site_url('news/'.$news_item['slug']); ?>\">View article</a></p>\n\n\t<?php endforeach; ?>\n\nHere, each news item is looped and displayed to the user. You can see we\nwrote our template in PHP mixed with HTML. If you prefer to use a template\nlanguage, you can use CodeIgniter's :doc:`Template\nParser <../libraries/parser>` class or a third party parser.\n\nThe news overview page is now done, but a page to display individual\nnews items is still absent. The model created earlier is made in such\nway that it can easily be used for this functionality. You only need to\nadd some code to the controller and create a new view. Go back to the\n``News`` controller and update ``view()`` with the following:\n\n::\n\n\tpublic function view($slug = NULL)\n\t{\n\t\t$data['news_item'] = $this->news_model->get_news($slug);\n\n\t\tif (empty($data['news_item']))\n\t\t{\n\t\t\tshow_404();\n\t\t}\n\n\t\t$data['title'] = $data['news_item']['title'];\n\n\t\t$this->load->view('templates/header', $data);\n\t\t$this->load->view('news/view', $data);\n\t\t$this->load->view('templates/footer');\n\t}\n\nInstead of calling the ``get_news()`` method without a parameter, the\n``$slug`` variable is passed, so it will return the specific news item.\nThe only things left to do is create the corresponding view at\n*application/views/news/view.php*. Put the following code in this file.\n\n::\n\n\t<?php\n\techo '<h2>'.$news_item['title'].'</h2>';\n\techo $news_item['text'];\n\nRouting\n-------\n\nBecause of the wildcard routing rule created earlier, you need an extra\nroute to view the controller that you just made. Modify your routing file\n(*application/config/routes.php*) so it looks as follows.\nThis makes sure the requests reaches the ``News`` controller instead of\ngoing directly to the ``Pages`` controller. The first line routes URI's\nwith a slug to the ``view()`` method in the ``News`` controller.\n\n::\n\n\t$route['news/(:any)'] = 'news/view/$1';\n\t$route['news'] = 'news';\n\t$route['(:any)'] = 'pages/view/$1';\n\t$route['default_controller'] = 'pages/view';\n\nPoint your browser to your document root, followed by index.php/news and\nwatch your news page.\n"
  },
  {
    "path": "user_guide_src/source/tutorial/static_pages.rst",
    "content": "############\nStatic pages\n############\n\n**Note:** This tutorial assumes you've downloaded CodeIgniter and\n:doc:`installed the framework <../installation/index>` in your\ndevelopment environment.\n\nThe first thing you're going to do is set up a **controller** to handle\nstatic pages. A controller is simply a class that helps delegate work.\nIt is the glue of your web application.\n\nFor example, when a call is made to:\n\n\thttp://example.com/news/latest/10\n\nWe might imagine that there is a controller named \"news\". The method\nbeing called on news would be \"latest\". The news method's job could be to\ngrab 10 news items, and render them on the page. Very often in MVC,\nyou'll see URL patterns that match:\n\n\thttp://example.com/[controller-class]/[controller-method]/[arguments]\n\nAs URL schemes become more complex, this may change. But for now, this\nis all we will need to know.\n\nCreate a file at *application/controllers/Pages.php* with the following\ncode.\n\n::\n\n\t<?php \n\tclass Pages extends CI_Controller { \n\n\t\tpublic function view($page = 'home') \n\t\t{\n\t        }\n\t}\n\nYou have created a class named ``Pages``, with a view method that accepts\none argument named ``$page``. The ``Pages`` class is extending the\n``CI_Controller`` class. This means that the new pages class can access the\nmethods and variables defined in the ``CI_Controller`` class\n(*system/core/Controller.php*).\n\nThe **controller is what will become the center of every request** to\nyour web application. In very technical CodeIgniter discussions, it may\nbe referred to as the *super object*. Like any php class, you refer to\nit within your controllers as ``$this``. Referring to ``$this`` is how\nyou will load libraries, views, and generally command the framework.\n\nNow you've created your first method, it's time to make some basic page\ntemplates. We will be creating two \"views\" (page templates) that act as\nour page footer and header.\n\nCreate the header at *application/views/templates/header.php* and add\nthe following code:\n\n::\n\n\t<html lang=\"en\">\n\t\t<head>\n\t\t\t<title>CodeIgniter Tutorial</title>\n\t\t</head>\n\t\t<body>\n\n\t\t\t<h1><?php echo $title; ?></h1>\n\nThe header contains the basic HTML code that you'll want to display\nbefore loading the main view, together with a heading. It will also\noutput the ``$title`` variable, which we'll define later in the controller.\nNow, create a footer at *application/views/templates/footer.php* that\nincludes the following code:\n\n::\n\n\t\t\t<em>&copy; 2015</em>\n\t\t</body>\n\t</html>\n\nAdding logic to the controller\n------------------------------\n\nEarlier you set up a controller with a ``view()`` method. The method\naccepts one parameter, which is the name of the page to be loaded. The\nstatic page templates will be located in the *application/views/pages/*\ndirectory.\n\nIn that directory, create two files named *home.php* and *about.php*.\nWithin those files, type some text − anything you'd like − and save them.\nIf you like to be particularly un-original, try \"Hello World!\".\n\nIn order to load those pages, you'll have to check whether the requested\npage actually exists:\n\n::\n\n\tpublic function view($page = 'home')\n\t{\n\t        if ( ! file_exists(APPPATH.'views/pages/'.$page.'.php'))\n\t\t{\n\t\t\t// Whoops, we don't have a page for that!\n\t\t\tshow_404();\n\t\t}\n\n\t\t$data['title'] = ucfirst($page); // Capitalize the first letter\n\n\t\t$this->load->view('templates/header', $data);\n\t\t$this->load->view('pages/'.$page, $data);\n\t\t$this->load->view('templates/footer', $data);\n\t}\n\nNow, when the page does exist, it is loaded, including the header and\nfooter, and displayed to the user. If the page doesn't exist, a \"404\nPage not found\" error is shown.\n\nThe first line in this method checks whether the page actually exists.\nPHP's native ``file_exists()`` function is used to check whether the file\nis where it's expected to be. ``show_404()`` is a built-in CodeIgniter\nfunction that renders the default error page.\n\nIn the header template, the ``$title`` variable was used to customize the\npage title. The value of title is defined in this method, but instead of\nassigning the value to a variable, it is assigned to the title element\nin the ``$data`` array.\n\nThe last thing that has to be done is loading the views in the order\nthey should be displayed. The second parameter in the ``view()`` method is\nused to pass values to the view. Each value in the ``$data`` array is\nassigned to a variable with the name of its key. So the value of\n``$data['title']`` in the controller is equivalent to ``$title`` in the\nview.\n\nRouting\n-------\n\nThe controller is now functioning! Point your browser to\n``[your-site-url]index.php/pages/view`` to see your page. When you visit\n``index.php/pages/view/about`` you'll see the about page, again including\nthe header and footer.\n\nUsing custom routing rules, you have the power to map any URI to any\ncontroller and method, and break free from the normal convention:\n``http://example.com/[controller-class]/[controller-method]/[arguments]``\n\nLet's do that. Open the routing file located at\n*application/config/routes.php* and add the following two lines.\nRemove all other code that sets any element in the ``$route`` array.\n\n::\n\n\t$route['default_controller'] = 'pages/view';\n\t$route['(:any)'] = 'pages/view/$1';\n\nCodeIgniter reads its routing rules from top to bottom and routes the\nrequest to the first matching rule. Each rule is a regular expression\n(left-side) mapped to a controller and method name separated by slashes\n(right-side). When a request comes in, CodeIgniter looks for the first\nmatch, and calls the appropriate controller and method, possibly with\narguments.\n\nMore information about routing can be found in the URI Routing\n:doc:`documentation <../general/routing>`.\n\nHere, the second rule in the ``$route`` array matches **any** request\nusing the wildcard string ``(:any)``. and passes the parameter to the\n``view()`` method of the ``Pages`` class.\n\nNow visit ``index.php/about``. Did it get routed correctly to the ``view()``\nmethod in the pages controller? Awesome!\n"
  }
]