From 6fec2464f8d0341163d49d1e5f8f6a7ed1faae02 Mon Sep 17 00:00:00 2001 From: objecttothis <17935339+objecttothis@users.noreply.github.com> Date: Tue, 14 Apr 2026 01:05:10 +0400 Subject: [PATCH] Update to CodeIgniter 4.7.2 (#4485) - Merge Config and Core File Changes 4.6.3 > 4.6.4 - Merge Config and Core File Changes 4.6.4 > 4.7.0 - Added app\Config\WorkerMode.php - Merge Config and Core File Changes Not previously merged - Added app\Config\Hostnames.php - Corrected incorrect CSS property used in invoice.php view. - Corrected unknown CSS properties used in register.php view. - Used shorthand CSS in debug.css - Corrected indentation in barcode_sheet.php view. - Corrected indentation in footer.php view. - Corrected indentation in invoice_email.php view. - Replaced obsolete attributes with CSS style attributes in barcode_sheet.php - Replaced obsolete attribute in error_exception.php - Replaced obsolete attribute in invoice_email.php - Replaced obsolete attribute in quote_email.php - Replaced obsolete attributes in work_order_email.php - Fixed indentation in system_info.php - Replaced tag outside

tags, which isn't allowed, with style attributes. - Simplified js return logic and indentation fixes in tax_categories.php - Simplified js return logic in tax_codes.php - Simplified js return logic in tax_jurisdictions.php - Removed unnecessary labels in manage views. - Rewrite JavaScript function and PHP to be more readable in bar.php, hbar.php, line.php and pie.php - Added type declarations, return types and an import to app\Config\Services - Updated Attribute.php parameter type - Updated Receiving_lib.php parameter type - Updated Receivings.php parameter types and updated PHPdocs - Updated tabular_helper.php parameter types and updated PHPdocs - Added type declarations and corrected PHPdocs in url_helper.php - Added return types to functions - Revert $objectSrc value in ContentSecurityPolicy.php - Correct return type in Customer->get_stats() - Correct return type in Item->get_info_by_id_or_number() - Correct misspelling in border-spacing - Added missing css style semicolons - Resolve operator precedence ambiguity. - Resolve column mismatch. - Added missing escaping in view. - Updated requirement for PHP 8.2 - Resolve unresolved conflicts - Added PHP 8.2 requirement to the README.md - Fixed bugs in display of UI - Fixed duplicated `>` in app\Views\Expenses\manage.php - Removed excess whitespace at the end of some lines in table_filter_persistence.php - Added missing `>` in app\Views\Expenses\manage.php - Corrected grammar in PHPdoc in table_filter_persistence.php - Remove bug causing `\` to be injected into the new giftcard value - Fix bug causing DROPDOWN Attribute Values to not save correctly - Added check for null in $normalizedItemId - Removing < PHP 8.2 from linting and tests - Update Linter to not include PHP 8.2 and 8.1 - Remove PHP 8.1 unit test cycle. - Update Bug Report Template - Update Composer files for CodeIgniter 4.7.2 - Updated INSTALL.md to reflect changes. --------- Signed-off-by: objec --- .github/ISSUE_TEMPLATE/bug report.yml | 17 +- .github/workflows/main.yml | 1 - .github/workflows/php-linter.yml | 8 - .github/workflows/phpunit.yml | 3 +- INSTALL.md | 2 +- README.md | 2 +- app/Config/App.php | 48 +- app/Config/Autoload.php | 2 - app/Config/Boot/testing.php | 29 +- app/Config/CURLRequest.php | 16 + app/Config/Cache.php | 53 +- app/Config/ContentSecurityPolicy.php | 50 +- app/Config/Cookie.php | 2 +- app/Config/Database.php | 43 +- app/Config/DocTypes.php | 3 - app/Config/Email.php | 5 + app/Config/Encryption.php | 17 + app/Config/Filters.php | 7 +- app/Config/Format.php | 9 + app/Config/Hostnames.php | 40 + app/Config/Images.php | 2 + app/Config/Logger.php | 3 +- app/Config/Migrations.php | 15 + app/Config/Mimes.php | 11 +- app/Config/Modules.php | 2 - app/Config/Optimize.php | 2 +- app/Config/Paths.php | 14 +- app/Config/Routing.php | 9 + app/Config/Security.php | 4 +- app/Config/Services.php | 11 +- app/Config/Session.php | 4 +- app/Config/Toolbar.php | 25 + app/Config/UserAgents.php | 10 + app/Config/View.php | 17 + app/Config/WorkerMode.php | 62 ++ app/Controllers/BaseController.php | 44 +- app/Controllers/Home.php | 6 +- app/Controllers/Receivings.php | 10 +- app/Controllers/Sales.php | 2 +- app/Helpers/tabular_helper.php | 23 +- app/Helpers/url_helper.php | 6 +- app/Libraries/MY_Language.php | 7 +- app/Libraries/Receiving_lib.php | 2 +- app/Models/Attribute.php | 45 +- app/Models/Customer.php | 3 +- app/Models/Employee.php | 3 +- app/Models/Item.php | 18 +- app/Views/barcodes/barcode_sheet.php | 55 +- app/Views/cashups/manage.php | 2 +- app/Views/configs/system_info.php | 24 +- app/Views/errors/cli/error_exception.php | 2 +- app/Views/errors/html/debug.css | 10 +- app/Views/errors/html/error_400.php | 33 +- app/Views/errors/html/error_404.php | 8 +- app/Views/errors/html/error_exception.php | 783 +++++++++--------- app/Views/expenses/manage.php | 6 +- app/Views/partial/footer.php | 27 +- .../partial/table_filter_persistence.php | 26 +- app/Views/reports/graphs/bar.php | 26 +- app/Views/reports/graphs/hbar.php | 28 +- app/Views/reports/graphs/line.php | 82 +- app/Views/reports/graphs/pie.php | 31 +- app/Views/sales/invoice.php | 38 +- app/Views/sales/invoice_email.php | 296 ++++--- app/Views/sales/manage.php | 2 +- app/Views/sales/quote_email.php | 4 +- app/Views/sales/register.php | 6 +- app/Views/sales/work_order_email.php | 4 +- app/Views/taxes/tax_categories.php | 22 +- app/Views/taxes/tax_codes.php | 18 +- app/Views/taxes/tax_jurisdictions.php | 18 +- composer.json | 6 +- composer.lock | 43 +- preload.php | 12 +- public/resources/.gitkeep | 0 75 files changed, 1316 insertions(+), 1013 deletions(-) create mode 100644 app/Config/Hostnames.php create mode 100644 app/Config/WorkerMode.php delete mode 100644 public/resources/.gitkeep diff --git a/.github/ISSUE_TEMPLATE/bug report.yml b/.github/ISSUE_TEMPLATE/bug report.yml index dec00a5e8..fddd1ddcc 100644 --- a/.github/ISSUE_TEMPLATE/bug report.yml +++ b/.github/ISSUE_TEMPLATE/bug report.yml @@ -12,11 +12,11 @@ body: attributes: value: | ## Thanks for taking the time to fill out this bug report! 🐜 - + Bug reports help us identify and fix issues. Please provide as much detail as possible. - + > ⚠️ **Important:** Submit a separate bug report for each problem you encounter. - > + > > 🚫 Do not include personal identifying information such as email addresses or encryption keys. # ───────────────────────────────────────────────────────────────────────────── @@ -28,7 +28,7 @@ body: label: πŸ› Bug Description description: A clear and concise description of what the bug is. placeholder: | - Example: When I try to print a receipt, the application crashes + Example: When I try to print a receipt, the application crashes with an error message saying "Unable to connect to printer". validations: required: true @@ -86,8 +86,7 @@ body: - PHP 8.2 - PHP 8.1 - PHP 7.4 - - PHP 7.3 - - PHP 7.2 + - Other default: 0 validations: required: true @@ -141,7 +140,7 @@ body: label: πŸ“Š System Information Report description: | Copy and paste the system information from OSPOS: - + **Navigation:** Configuration β†’ Setup & Conf β†’ System Info placeholder: | Paste the System Information Report here... @@ -155,7 +154,7 @@ body: label: πŸ“œ Relevant Log Output description: | Please copy and paste any relevant log output. - + **Log locations:** - OSPOS logs: `writable/logs/` - Web server logs: `/var/log/apache2/` or `/var/log/nginx/` @@ -185,4 +184,4 @@ body: - label: I have searched existing issues to ensure this bug has not already been reported required: true - label: I have provided all the information requested above - required: true \ No newline at end of file + required: true diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f6dd73190..4d823dc3f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -28,7 +28,6 @@ jobs: fail-fast: false matrix: php-version: - - '8.1' - '8.2' - '8.3' - '8.4' diff --git a/.github/workflows/php-linter.yml b/.github/workflows/php-linter.yml index c27f5535c..fb94c5b86 100644 --- a/.github/workflows/php-linter.yml +++ b/.github/workflows/php-linter.yml @@ -12,14 +12,6 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - - name: PHP Lint 8.0 - uses: dbfx/github-phplint/8.0@master - with: - folder-to-exclude: "! -path \"./vendor/*\" ! -path \"./folder/excluded/*\"" - - name: PHP Lint 8.1 - uses: dbfx/github-phplint/8.1@master - with: - folder-to-exclude: "! -path \"./vendor/*\" ! -path \"./folder/excluded/*\"" - name: PHP Lint 8.2 uses: dbfx/github-phplint/8.2@master with: diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 2198acaf8..3fe599ca6 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -34,7 +34,6 @@ jobs: fail-fast: false matrix: php-version: - - '8.1' - '8.2' - '8.3' - '8.4' @@ -119,4 +118,4 @@ jobs: - name: Stop MariaDB if: always() - run: docker stop mysql && docker rm mysql \ No newline at end of file + run: docker stop mysql && docker rm mysql diff --git a/INSTALL.md b/INSTALL.md index 32edff423..b688cf972 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,6 +1,6 @@ ## Server Requirements -- PHP version `8.1` to `8.4` are supported, PHP version `≀7.4` is NOT supported. Please note that PHP needs to have the extensions `php-json`, `php-gd`, `php-bcmath`, `php-intl`, `php-openssl`, `php-mbstring`, `php-curl` and `php-xml` installed and enabled. An unstable master build can be downloaded in the releases section. +- PHP version `8.2` to `8.4` are supported, PHP version `≀ 8.1` is NOT supported. Please note that PHP needs to have the extensions `php-json`, `php-gd`, `php-bcmath`, `php-intl`, `php-openssl`, `php-mbstring`, `php-curl` and `php-xml` installed and enabled. An unstable master build can be downloaded in the releases section. - MySQL `5.7` is supported, also MariaDB replacement `10.x` is supported and might offer better performance. - Apache `2.4` is supported. Nginx should work fine too, see [wiki page here](https://github.com/opensourcepos/opensourcepos/wiki/Local-Deployment-using-LEMP). - Raspberry PI based installations proved to work, see [wiki page here](). diff --git a/README.md b/README.md index 1a46a4386..56c47dd17 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ NOTE: If you're running non-release code, please make sure you always run the la - If you have suhosin installed and face an issue with CSRF, please make sure you read [issue #1492](https://github.com/opensourcepos/opensourcepos/issues/1492). -- PHP `β‰₯ 8.1` is required to run this app. +- PHP `β‰₯ 8.2` is required to run this app. ## πŸƒ Keep the Machine Running diff --git a/app/Config/App.php b/app/Config/App.php index 6ecff69eb..db4b0a876 100644 --- a/app/Config/App.php +++ b/app/Config/App.php @@ -55,21 +55,13 @@ class App extends BaseConfig public string $baseURL; // Defined in the constructor /** - * Allowed Hostnames for the Site URL. - * - * Security: This is used to validate the HTTP Host header to prevent - * Host Header Injection attacks. If the Host header doesn't match - * an entry in this list, the request will use the first allowed hostname. - * - * IMPORTANT: This MUST be configured for production deployments. - * If empty in production, the application will fail to start. - * In development, it will fall back to 'localhost' with a warning. - * - * Configure via .env file (comma-separated list): - * app.allowedHostnames = 'example.com,www.example.com' - * - * For local development: - * app.allowedHostnames = 'localhost' + * Allowed Hostnames in the Site URL other than the hostname in the baseURL. + * If you want to accept multiple Hostnames, set this. + * + * E.g., + * When your site URL ($baseURL) is 'http://example.com/', and your site + * also accepts 'http://media.example.com/' and 'http://accounts.example.com/': + * ['media.example.com', 'accounts.example.com'] * * @var list */ @@ -125,7 +117,7 @@ class App extends BaseConfig | DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!! | */ - public string $permittedURIChars = 'a-z 0-9~%.:_\-='; + public string $permittedURIChars = 'a-z 0-9~%.:_\-'; /** * -------------------------------------------------------------------------- @@ -286,12 +278,12 @@ class App extends BaseConfig * @see http://www.html5rocks.com/en/tutorials/security/content-security-policy/ * @see http://www.w3.org/TR/CSP/ */ - public bool $CSPEnabled = false; // TODO: Currently CSP3 tags are not supported so enabling this causes problems with script-src-elem, style-src-attr and style-src-elem + public bool $CSPEnabled = false; public function __construct() { parent::__construct(); - + // Solution for CodeIgniter 4 limitation: arrays cannot be set from .env // See: https://github.com/codeigniter4/CodeIgniter4/issues/7311 $envAllowedHostnames = getenv('app.allowedHostnames'); @@ -301,9 +293,9 @@ class App extends BaseConfig static fn (string $hostname): bool => $hostname !== '' )); } - + $this->https_on = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') || (isset($_ENV['FORCE_HTTPS']) && $_ENV['FORCE_HTTPS'] == 'true'); - + $host = $this->getValidHost(); $this->baseURL = $this->https_on ? 'https' : 'http'; $this->baseURL .= '://' . $host . '/'; @@ -312,39 +304,39 @@ class App extends BaseConfig /** * Validates and returns a trusted hostname. - * + * * Security: Prevents Host Header Injection attacks (GHSA-jchf-7hr6-h4f3) * by validating the HTTP_HOST against a whitelist of allowed hostnames. - * + * * In production: Fails fast if allowedHostnames is not configured. * In development: Allows localhost fallback with an error log. - * + * * @return string A validated hostname * @throws \RuntimeException If allowedHostnames is not configured in production */ private function getValidHost(): string { $httpHost = $_SERVER['HTTP_HOST'] ?? 'localhost'; - + // Determine environment // CodeIgniter's test bootstrap sets $_SERVER['CI_ENVIRONMENT'] = 'testing' // Check $_SERVER first, then $_ENV, then fall back to 'production' $environment = $_SERVER['CI_ENVIRONMENT'] ?? $_ENV['CI_ENVIRONMENT'] ?? getenv('CI_ENVIRONMENT') ?: 'production'; if (empty($this->allowedHostnames)) { - $errorMessage = + $errorMessage = 'Security: allowedHostnames is not configured. ' . 'Host header injection protection is disabled. ' . 'Set app.allowedHostnames in your .env file. ' . 'Example: app.allowedHostnames = "example.com,www.example.com" ' . 'Received Host: ' . $httpHost; - + // Production: Fail explicitly to prevent silent security vulnerabilities // Testing and development: Allow localhost fallback if ($environment === 'production') { throw new \RuntimeException($errorMessage); } - + log_message('error', $errorMessage . ' Using localhost fallback (development only).'); return 'localhost'; } @@ -354,7 +346,7 @@ class App extends BaseConfig } // Host not in whitelist - use first configured hostname as fallback - log_message('warning', + log_message('warning', 'Security: Rejected HTTP_HOST "' . $httpHost . '" - not in allowedHostnames whitelist. ' . 'Using fallback: ' . $this->allowedHostnames[0] ); diff --git a/app/Config/Autoload.php b/app/Config/Autoload.php index 87c815f8f..073b39aab 100644 --- a/app/Config/Autoload.php +++ b/app/Config/Autoload.php @@ -17,8 +17,6 @@ use CodeIgniter\Config\AutoloadConfig; * * NOTE: This class is required prior to Autoloader instantiation, * and does not extend BaseConfig. - * - * @immutable */ class Autoload extends AutoloadConfig { diff --git a/app/Config/Boot/testing.php b/app/Config/Boot/testing.php index 02fd04a2b..40b6ca83c 100644 --- a/app/Config/Boot/testing.php +++ b/app/Config/Boot/testing.php @@ -1,23 +1,38 @@ + * + * @see https://www.php.net/manual/en/curl.constants.php#constant.curl-lock-data-connect + */ + public array $shareConnectionOptions = [ + CURL_LOCK_DATA_CONNECT, + CURL_LOCK_DATA_DNS, + ]; + /** * -------------------------------------------------------------------------- * CURLRequest Share Options diff --git a/app/Config/Cache.php b/app/Config/Cache.php index a05ca78c1..0e0dfca9e 100644 --- a/app/Config/Cache.php +++ b/app/Config/Cache.php @@ -3,6 +3,7 @@ namespace Config; use CodeIgniter\Cache\CacheInterface; +use CodeIgniter\Cache\Handlers\ApcuHandler; use CodeIgniter\Cache\Handlers\DummyHandler; use CodeIgniter\Cache\Handlers\FileHandler; use CodeIgniter\Cache\Handlers\MemcachedHandler; @@ -78,7 +79,7 @@ class Cache extends BaseConfig * Your file storage preferences can be specified below, if you are using * the File driver. * - * @var array + * @var array{storePath?: string, mode?: int} */ public array $file = [ 'storePath' => WRITEPATH . 'cache/', @@ -95,7 +96,7 @@ class Cache extends BaseConfig * * @see https://codeigniter.com/user_guide/libraries/caching.html#memcached * - * @var array + * @var array{host?: string, port?: int, weight?: int, raw?: bool} */ public array $memcached = [ 'host' => '127.0.0.1', @@ -108,17 +109,28 @@ class Cache extends BaseConfig * ------------------------------------------------------------------------- * Redis settings * ------------------------------------------------------------------------- + * * Your Redis server can be specified below, if you are using * the Redis or Predis drivers. * - * @var array + * @var array{ + * host?: string, + * password?: string|null, + * port?: int, + * timeout?: int, + * async?: bool, + * persistent?: bool, + * database?: int + * } */ public array $redis = [ - 'host' => '127.0.0.1', - 'password' => null, - 'port' => 6379, - 'timeout' => 0, - 'database' => 0, + 'host' => '127.0.0.1', + 'password' => null, + 'port' => 6379, + 'timeout' => 0, + 'async' => false, // specific to Predis and ignored by the native Redis extension + 'persistent' => false, + 'database' => 0, ]; /** @@ -132,6 +144,7 @@ class Cache extends BaseConfig * @var array> */ public array $validHandlers = [ + 'apcu' => ApcuHandler::class, 'dummy' => DummyHandler::class, 'file' => FileHandler::class, 'memcached' => MemcachedHandler::class, @@ -158,4 +171,28 @@ class Cache extends BaseConfig * @var bool|list */ public $cacheQueryString = false; + + /** + * -------------------------------------------------------------------------- + * Web Page Caching: Cache Status Codes + * -------------------------------------------------------------------------- + * + * HTTP status codes that are allowed to be cached. Only responses with + * these status codes will be cached by the PageCache filter. + * + * Default: [] - Cache all status codes (backward compatible) + * + * Recommended: [200] - Only cache successful responses + * + * You can also use status codes like: + * [200, 404, 410] - Cache successful responses and specific error codes + * [200, 201, 202, 203, 204] - All 2xx successful responses + * + * WARNING: Using [] may cache temporary error pages (404, 500, etc). + * Consider restricting to [200] for production applications to avoid + * caching errors that should be temporary. + * + * @var list + */ + public array $cacheStatusCodes = []; } diff --git a/app/Config/ContentSecurityPolicy.php b/app/Config/ContentSecurityPolicy.php index 5a46774af..bdf7c2ba1 100644 --- a/app/Config/ContentSecurityPolicy.php +++ b/app/Config/ContentSecurityPolicy.php @@ -30,6 +30,11 @@ class ContentSecurityPolicy extends BaseConfig */ public ?string $reportURI = null; + /** + * Specifies a reporting endpoint to which violation reports ought to be sent. + */ + public ?string $reportTo = null; + /** * Instructs user agents to rewrite URL schemes, changing * HTTP to HTTPS. This directive is for websites with @@ -38,12 +43,12 @@ class ContentSecurityPolicy extends BaseConfig public bool $upgradeInsecureRequests = false; // ------------------------------------------------------------------------- - // Sources allowed + // CSP DIRECTIVES SETTINGS // NOTE: once you set a policy to 'none', it cannot be further restricted // ------------------------------------------------------------------------- /** - * Will default to self if not overridden + * Will default to `'self'` if not overridden * * @var list|string|null */ @@ -64,6 +69,21 @@ class ContentSecurityPolicy extends BaseConfig 'www.google.com www.gstatic.com' ]; + /** + * Specifies valid sources for JavaScript - - + + + - -

-
- Displayed at — - PHP: — - CodeIgniter: -- - Environment: + +
+
+ Displayed at — + PHP: — + CodeIgniter: -- + Environment: +
+
+

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

+

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

+
+ +
-

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

-

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

+

at line

+ + +
+ +
+
-
- -
-

at line

- - -
- -
- -
- -
- getPrevious()) { - $last = $prevException; - ?> - -
-                Caused by:
-                getCode() ? ' #' . $prevException->getCode() : '') ?>
-
-                getMessage())) ?>
-                getMessage())) ?>" rel="noreferrer" target="_blank">search →
-                getFile()) . ':' . $prevException->getLine()) ?>
-            
- - -
- - -
- - - -
- - -
- -
    - $row) : ?> - -
  1. -

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

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

    - - - -
    - -
    - -
  2. - - -
- -
- - -
- - - -

$

- - - - - - - - - - $value) : ?> - - - - - - -
KeyValue
- - - -
- -
- - - - - - -

Constants

- - - - - - - - - - $value) : ?> - - - - - - -
KeyValue
- - - -
- -
- -
- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PathgetUri()) ?>
HTTP MethodgetMethod()) ?>
IP AddressgetIPAddress()) ?>
Is AJAX Request?isAJAX() ? 'yes' : 'no' ?>
Is CLI Request?isCLI() ? 'yes' : 'no' ?>
Is Secure Request?isSecure() ? 'yes' : 'no' ?>
User AgentgetUserAgent()->getAgentString()) ?>
- - - - - - - - -

$

- - - - - - - - - - $value) : ?> - - - - - - -
KeyValue
- - - -
- -
- - - - - -
- No $_GET, $_POST, or $_COOKIE Information to show. -
- - - - headers(); ?> - - -

Headers

- - - - - - - - - - $value) : ?> - - - - - - -
HeaderValue
- getValueLine(), 'html'); - } else { - foreach ($value as $i => $header) { - echo ' ('. $i+1 . ') ' . esc($header->getValueLine(), 'html'); - } - } - ?> -
- - -
- - +
setStatusCode(http_response_code()); - ?> -
- - - - - -
Response StatusgetStatusCode() . ' - ' . $response->getReasonPhrase()) ?>
+ $last = $exception; - headers(); ?> - -

Headers

+ while ($prevException = $last->getPrevious()) { + $last = $prevException; + ?> + +
+        Caused by:
+        getCode() ? ' #' . $prevException->getCode() : '') ?>
+
+        getMessage())) ?>
+        getMessage())) ?>"
+           rel="noreferrer" target="_blank">search →
+        getFile()) . ':' . $prevException->getLine()) ?>
+        
+ + +
+ + +
+ + + +
+ + +
+ +
    + $row) : ?> + +
  1. +

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

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

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

$

+ + + + + + + + + + $value) : ?> + + + + + + +
KeyValue
+ + + +
+ +
+ + + + + + +

Constants

+ + + + + + + + + + $value) : ?> + + + + + + +
KeyValue
+ + + +
+ +
+ +
+ + +
+ - - - - - - - $value) : ?> - - + + - + + + + + + + + + + + + + + + + + + + + + + + + +
HeaderValue
- getHeaderLine($name), 'html'); - } else { - foreach ($value as $i => $header) { - echo ' ('. $i+1 . ') ' . esc($header->getValueLine(), 'html'); - } - } - ?> - PathgetUri()) ?>
HTTP MethodgetMethod()) ?>
IP AddressgetIPAddress()) ?>
Is AJAX Request?isAJAX() ? 'yes' : 'no' ?>
Is CLI Request?isCLI() ? 'yes' : 'no' ?>
Is Secure Request?isSecure() ? 'yes' : 'no' ?>
User AgentgetUserAgent()->getAgentString()) ?>
- -
- -
- + + + -
    - -
  1. - -
-
+ - -
+

$

- - +
+ + + + + + + + $value) : ?> + + + + + + +
KeyValue
+ + + +
+ +
+ + + + + +
+ No $_GET, $_POST, or $_COOKIE Information to show. +
+ + + + headers(); ?> + + +

Headers

+ + + + + + + + + + $value) : ?> + + + + + + +
HeaderValue
+ getValueLine(), 'html'); + } else { + foreach ($value as $i => $header) { + echo ' (' . ($i + 1) . ') ' . esc($header->getValueLine(), 'html'); + } + } + ?> +
+ + +
+ + + setStatusCode(http_response_code()); + ?> +
+ - - + + - - - - - - - - - -
Memory UsageResponse StatusgetStatusCode() . ' - ' . $response->getReasonPhrase()) ?>
Peak Memory Usage:
Memory Limit:
+ -
+ headers(); ?> + +

Headers

-
+ + + + + + + + + $value) : ?> + + + + + + +
HeaderValue
+ getHeaderLine($name), 'html'); + } else { + foreach ($value as $i => $header) { + echo ' (' . ($i + 1) . ') ' . esc($header->getValueLine(), 'html'); + } + } + ?> +
-
- + +
- + +
+ + +
    + +
  1. + +
+
+ + +
+ + + + + + + + + + + + + + + + +
Memory Usage
Peak Memory Usage:
Memory Limit:
+ +
+ +
+ +
+ + + diff --git a/app/Views/expenses/manage.php b/app/Views/expenses/manage.php index f3bfd12dd..945429d7d 100644 --- a/app/Views/expenses/manage.php +++ b/app/Views/expenses/manage.php @@ -49,13 +49,13 @@ }); }); -> + + false, 'selected_printer' => 'takings_printer']) ?>
- - '; - exit; - } - ?> - -
- - - - - - - - - - -
- -
-
- -
- - - - - - - - - - 0) { ?> - - - - - -
-
- - - - - - - - - 0) { - $invoice_columns = $invoice_columns + 1; - ?> - - - - - - $item) { - if ($item['print_option'] == PRINT_YES) { - ?> - - - - - - - 0): ?> - - - - - + + <?= lang('Sales.email_receipt') ?> + + + + ' . esc($error_message) . ''; + exit; } - ?> + ?> - - - +
+ +
+ + + + + + + + +
+ +
+
+ +
+ + + + + + + + + + 0) { ?> + + + + + +
+
- - - - - + + + + + + + + 0) { + $invoice_columns = $invoice_columns + 1; + ?> + + + + + + $item) { + if ($item['print_option'] == PRINT_YES) { + ?> + + + + + + + 0): ?> + + + + + + + + + - $tax) { ?> - - + + - - - - - - + $tax) { ?> + + + + + + - $payment) { - $only_sale_check |= $payment['payment_type'] == lang('Sales.check'); - $splitpayment = explode(':', $payment['payment_type']); // TODO: $splitpayment does not meet the variable naming conventions for this project - $show_giftcard_remainder |= $splitpayment[0] == lang('Sales.giftcard'); - ?> - - + + - - - - - - - - + - - - - - - -
= 0 ? ($only_sale_check ? 'Sales.check_balance' : 'Sales.change_due') : 'Sales.amount_due') ?>
+ foreach ($payments as $payment_id => $payment) { + $only_sale_check |= $payment['payment_type'] == lang('Sales.check'); + $splitpayment = explode(':', $payment['payment_type']); // TODO: $splitpayment does not meet the variable naming conventions for this project + $show_giftcard_remainder |= $splitpayment[0] == lang('Sales.giftcard'); + ?> + + + + + + -
-
-
- - -
- -
-
- <?= esc($sale_id) ?>
- + + + + + + + + + + + + = 0 ? ($only_sale_check ? 'Sales.check_balance' : 'Sales.change_due') : 'Sales.amount_due') ?> + + + + + +
+
+
+ + +
+ +
+
+ <?= esc($sale_id) ?>
+ +
-
- - - + diff --git a/app/Views/sales/manage.php b/app/Views/sales/manage.php index dc5eacef2..d3d9ceaaf 100644 --- a/app/Views/sales/manage.php +++ b/app/Views/sales/manage.php @@ -73,7 +73,7 @@ false, 'selected_printer' => 'takings_printer']) ?> - +
@@ -116,7 +116,7 @@ ?> - + diff --git a/app/Views/sales/register.php b/app/Views/sales/register.php index 12fc10c08..6b4f275e8 100644 --- a/app/Views/sales/register.php +++ b/app/Views/sales/register.php @@ -173,12 +173,12 @@ helper('url'); - - - diff --git a/app/Views/sales/work_order_email.php b/app/Views/sales/work_order_email.php index 22da1c75d..0156f2ccc 100644 --- a/app/Views/sales/work_order_email.php +++ b/app/Views/sales/work_order_email.php @@ -55,7 +55,7 @@
'item_number', 'id' => 'item_number', 'class' => 'form-control input-sm', 'value' => $item['item_number'], 'tabindex' => ++$tabindex]) ?> + 'name', 'id' => 'name', 'class' => 'form-control input-sm', 'value' => $item['name'], 'tabindex' => ++$tabindex]) ?> +
'hidden', 'name' => 'item_id', 'value' => $item['item_id']]) ?> + 'item_description', 'id' => 'item_description', 'class' => 'form-control input-sm', 'value' => $item['description'], 'tabindex' => ++$tabindex]) ?> - +
@@ -103,7 +103,7 @@ ?> - + diff --git a/app/Views/taxes/tax_categories.php b/app/Views/taxes/tax_categories.php index 64b57e508..5ebc4b98a 100644 --- a/app/Views/taxes/tax_categories.php +++ b/app/Views/taxes/tax_categories.php @@ -79,24 +79,18 @@ $('input[name="tax_category[]"]').each(function() { value_count = $(this).val() == value ? value_count + 1 : value_count; }); - if (value_count > 1) { - return false; - } - return true; + return value_count <= 1; + }, ""); $.validator.addMethod('validateTaxCategoryCharacters', function(value, element) { - if ((value.indexOf('_') != -1)) { - return false; - } - return true; + return (value.indexOf('_') == -1); + }, ""); $.validator.addMethod('requireTaxCategory', function(value, element) { - if (value.trim() == '') { - return false; - } - return true; + return value.trim() != ''; + }, ""); $('#tax_categories_form').validate($.extend(form_support.handler, { @@ -120,8 +114,8 @@ })); $tax_category_data) { + $i = 0; + foreach ($tax_categories as $tax_category => $tax_category_data) { ?> $('').rules("add", { requireTaxCategory: true, diff --git a/app/Views/taxes/tax_codes.php b/app/Views/taxes/tax_codes.php index cae03bff7..5de1b09b4 100644 --- a/app/Views/taxes/tax_codes.php +++ b/app/Views/taxes/tax_codes.php @@ -79,24 +79,18 @@ $("input[name='tax_code[]']").each(function() { value_count = $(this).val() == value ? value_count + 1 : value_count; }); - if (value_count > 1) { - return false; - } - return true; + return value_count <= 1; + }, ""); $.validator.addMethod('validateTaxCodeCharacters', function(value, element) { - if ((value.indexOf('_') != -1)) { - return false; - } - return true; + return (value.indexOf('_') == -1); + }, ""); $.validator.addMethod('requireTaxCode', function(value, element) { - if (value.trim() == '') { - return false; - } - return true; + return value.trim() != ''; + }, ""); $('#tax_codes_form').validate($.extend(form_support.handler, { diff --git a/app/Views/taxes/tax_jurisdictions.php b/app/Views/taxes/tax_jurisdictions.php index 8b5c312a3..63469d3bc 100644 --- a/app/Views/taxes/tax_jurisdictions.php +++ b/app/Views/taxes/tax_jurisdictions.php @@ -83,24 +83,18 @@ $("input[name='jurisdiction_name[]']").each(function() { value_count = $(this).val() == value ? value_count + 1 : value_count; }); - if (value_count > 1) { - return false; - } - return true; + return value_count <= 1; + }, ""); $.validator.addMethod('validateTaxJurisdictionCharacters', function(value, element) { - if ((value.indexOf('_') != -1)) { - return false; - } - return true; + return (value.indexOf('_') == -1); + }, ""); $.validator.addMethod('requireTaxJurisdiction', function(value, element) { - if (value.trim() == '') { - return false; - } - return true; + return value.trim() != ''; + }, ""); $('#tax_jurisdictions_form').validate($.extend(form_support.handler, { diff --git a/composer.json b/composer.json index 2c578c24a..9b782a7a8 100644 --- a/composer.json +++ b/composer.json @@ -32,11 +32,11 @@ }, "require": { "ext-intl": "*", - "php": "^8.1", - "codeigniter4/framework": "4.6.3", + "php": "^8.2", + "codeigniter4/framework": "4.7.2", "dompdf/dompdf": "^2.0.3", "ezyang/htmlpurifier": "^4.17", - "laminas/laminas-escaper": "2.17.0", + "laminas/laminas-escaper": "2.18.0", "paragonie/random_compat": "^2.0.21", "picqer/php-barcode-generator": "^2.4.0", "tamtamchik/namecase": "^3.0.0" diff --git a/composer.lock b/composer.lock index b8a071337..8a9a1d69a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,40 +4,41 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9cf76605c1b45f81c547fefc5a29f101", + "content-hash": "e95f6e5e86d323370ddb0df57c4d3fb3", "packages": [ { "name": "codeigniter4/framework", - "version": "v4.6.3", + "version": "v4.7.2", "source": { "type": "git", "url": "https://github.com/codeigniter4/framework.git", - "reference": "68d1a5896106f869452dd369a690dd5bc75160fb" + "reference": "b3359be849be29394660c3aed909aa32b6c45cf6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/codeigniter4/framework/zipball/68d1a5896106f869452dd369a690dd5bc75160fb", - "reference": "68d1a5896106f869452dd369a690dd5bc75160fb", + "url": "https://api.github.com/repos/codeigniter4/framework/zipball/b3359be849be29394660c3aed909aa32b6c45cf6", + "reference": "b3359be849be29394660c3aed909aa32b6c45cf6", "shasum": "" }, "require": { "ext-intl": "*", "ext-mbstring": "*", - "laminas/laminas-escaper": "^2.17", - "php": "^8.1", + "laminas/laminas-escaper": "^2.18", + "php": "^8.2", "psr/log": "^3.0" }, "require-dev": { "codeigniter/coding-standard": "^1.7", "fakerphp/faker": "^1.24", "friendsofphp/php-cs-fixer": "^3.47.1", - "kint-php/kint": "^6.0", + "kint-php/kint": "^6.1", "mikey179/vfsstream": "^1.6.12", "nexusphp/cs-config": "^3.6", "phpunit/phpunit": "^10.5.16 || ^11.2", "predis/predis": "^3.0" }, "suggest": { + "ext-apcu": "If you use Cache class ApcuHandler", "ext-curl": "If you use CURLRequest class", "ext-dom": "If you use TestResponse", "ext-exif": "If you run Image class tests", @@ -49,7 +50,9 @@ "ext-memcached": "If you use Cache class MemcachedHandler with Memcached", "ext-mysqli": "If you use MySQL", "ext-oci8": "If you use Oracle Database", + "ext-pcntl": "If you use Signals", "ext-pgsql": "If you use PostgreSQL", + "ext-posix": "If you use Signals", "ext-readline": "Improves CLI::input() usability", "ext-redis": "If you use Cache class RedisHandler", "ext-simplexml": "If you format XML", @@ -78,7 +81,7 @@ "slack": "https://codeigniterchat.slack.com", "source": "https://github.com/codeigniter4/CodeIgniter4" }, - "time": "2025-08-02T13:36:13+00:00" + "time": "2026-03-24T18:26:09+00:00" }, { "name": "dompdf/dompdf", @@ -205,32 +208,32 @@ }, { "name": "laminas/laminas-escaper", - "version": "2.17.0", + "version": "2.18.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-escaper.git", - "reference": "df1ef9503299a8e3920079a16263b578eaf7c3ba" + "reference": "06f211dfffff18d91844c1f55250d5d13c007e18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/df1ef9503299a8e3920079a16263b578eaf7c3ba", - "reference": "df1ef9503299a8e3920079a16263b578eaf7c3ba", + "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/06f211dfffff18d91844c1f55250d5d13c007e18", + "reference": "06f211dfffff18d91844c1f55250d5d13c007e18", "shasum": "" }, "require": { "ext-ctype": "*", "ext-mbstring": "*", - "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "conflict": { "zendframework/zend-escaper": "*" }, "require-dev": { - "infection/infection": "^0.29.8", - "laminas/laminas-coding-standard": "~3.0.1", - "phpunit/phpunit": "^10.5.45", - "psalm/plugin-phpunit": "^0.19.2", - "vimeo/psalm": "^6.6.2" + "infection/infection": "^0.31.0", + "laminas/laminas-coding-standard": "~3.1.0", + "phpunit/phpunit": "^11.5.42", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13.1" }, "type": "library", "autoload": { @@ -262,7 +265,7 @@ "type": "community_bridge" } ], - "time": "2025-05-06T19:29:36+00:00" + "time": "2025-10-14T18:31:13+00:00" }, { "name": "masterminds/html5", diff --git a/preload.php b/preload.php index 0b4531223..288b30b4e 100644 --- a/preload.php +++ b/preload.php @@ -9,6 +9,9 @@ * the LICENSE file that was distributed with this source code. */ +use CodeIgniter\Boot; +use Config\Paths; + /* *--------------------------------------------------------------- * Sample file for Preloading @@ -54,6 +57,7 @@ class preload '/system/Config/Routes.php', '/system/Language/', '/system/bootstrap.php', + '/system/util_bootstrap.php', '/system/rewrite.php', '/Views/', // Errors occur. @@ -69,10 +73,10 @@ class preload private function loadAutoloader(): void { - $paths = new Config\Paths(); + $paths = new Paths(); require rtrim($paths->systemDirectory, '\\/ ') . DIRECTORY_SEPARATOR . 'Boot.php'; - CodeIgniter\Boot::preload($paths); + Boot::preload($paths); } /** @@ -97,7 +101,9 @@ class preload } require_once $file[0]; - echo 'Loaded: ' . $file[0] . "\n"; + // Uncomment only for debugging (to inspect which files are included). + // Never use this in production - preload scripts must not generate output. + // echo 'Loaded: ' . $file[0] . "\n"; } } } diff --git a/public/resources/.gitkeep b/public/resources/.gitkeep deleted file mode 100644 index e69de29bb..000000000