diff --git a/app/composer.json b/app/composer.json index ac9997dc8..2a74610e9 100644 --- a/app/composer.json +++ b/app/composer.json @@ -7,7 +7,7 @@ "require": { "php": ">=5.6", "adodb/adodb-php": "^5.20", - "cakephp/cakephp": "3.6.*", + "cakephp/cakephp": "3.8.*", "cakephp/migrations": "^1.0", "cakephp/plugin-installer": "^1.0", "doctrine/dbal": "^2.9", diff --git a/app/composer.lock b/app/composer.lock index 1046ed046..fd9031f48 100644 --- a/app/composer.lock +++ b/app/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "b9b672e596c49bbd0ec228cbdf26f427", + "content-hash": "8eba2ba5dae15d770db8f39b29644d0a", "packages": [ { "name": "adodb/adodb-php", @@ -108,16 +108,16 @@ }, { "name": "cakephp/cakephp", - "version": "3.6.11", + "version": "3.8.3", "source": { "type": "git", "url": "https://github.com/cakephp/cakephp.git", - "reference": "ddbfcdb479cfaa59f6e8c3433bf85aa71bff492d" + "reference": "705b43b70e87ec226a6b37099013de1003a17c17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/cakephp/zipball/ddbfcdb479cfaa59f6e8c3433bf85aa71bff492d", - "reference": "ddbfcdb479cfaa59f6e8c3433bf85aa71bff492d", + "url": "https://api.github.com/repos/cakephp/cakephp/zipball/705b43b70e87ec226a6b37099013de1003a17c17", + "reference": "705b43b70e87ec226a6b37099013de1003a17c17", "shasum": "" }, "require": { @@ -127,6 +127,7 @@ "ext-mbstring": "*", "php": ">=5.6.0", "psr/log": "^1.0.0", + "psr/simple-cache": "^1.0.0", "zendframework/zend-diactoros": "^1.4.0" }, "conflict": { @@ -149,9 +150,11 @@ }, "require-dev": { "cakephp/cakephp-codesniffer": "^3.0", + "cakephp/chronos": "^1.2.1", "phpunit/phpunit": "^5.7.14|^6.0" }, "suggest": { + "ext-curl": "To enable more efficient network calls in Http\\Client.", "ext-openssl": "To use Security::encrypt() or have secure CSRF token generation.", "lib-ICU": "The intl PHP library, to use Text::transliterate() or Text::slug()" }, @@ -190,20 +193,20 @@ "rapid-development", "validation" ], - "time": "2018-09-03T01:33:56+00:00" + "time": "2019-08-31T02:05:56+00:00" }, { "name": "cakephp/chronos", - "version": "1.2.2", + "version": "1.2.8", "source": { "type": "git", "url": "https://github.com/cakephp/chronos.git", - "reference": "30f5b26bcf76a5e53ecc274700ad1ec49dc05567" + "reference": "0292f06e8cc23fc82f0574889da2d8bf27b613c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/chronos/zipball/30f5b26bcf76a5e53ecc274700ad1ec49dc05567", - "reference": "30f5b26bcf76a5e53ecc274700ad1ec49dc05567", + "url": "https://api.github.com/repos/cakephp/chronos/zipball/0292f06e8cc23fc82f0574889da2d8bf27b613c1", + "reference": "0292f06e8cc23fc82f0574889da2d8bf27b613c1", "shasum": "" }, "require": { @@ -247,7 +250,7 @@ "datetime", "time" ], - "time": "2018-07-11T18:51:56+00:00" + "time": "2019-06-17T15:19:18+00:00" }, { "name": "cakephp/migrations", @@ -788,16 +791,16 @@ }, { "name": "psr/log", - "version": "1.0.2", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", + "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", "shasum": "" }, "require": { @@ -831,7 +834,55 @@ "psr", "psr-3" ], - "time": "2016-10-10T12:19:37+00:00" + "time": "2018-11-20T15:27:04+00:00" + }, + { + "name": "psr/simple-cache", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "time": "2017-10-23T01:57:42+00:00" }, { "name": "robmorgan/phinx", @@ -1316,16 +1367,16 @@ }, { "name": "zendframework/zend-diactoros", - "version": "1.8.6", + "version": "1.8.7", "source": { "type": "git", "url": "https://github.com/zendframework/zend-diactoros.git", - "reference": "20da13beba0dde8fb648be3cc19765732790f46e" + "reference": "a85e67b86e9b8520d07e6415fcbcb8391b44a75b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/20da13beba0dde8fb648be3cc19765732790f46e", - "reference": "20da13beba0dde8fb648be3cc19765732790f46e", + "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/a85e67b86e9b8520d07e6415fcbcb8391b44a75b", + "reference": "a85e67b86e9b8520d07e6415fcbcb8391b44a75b", "shasum": "" }, "require": { @@ -1345,9 +1396,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8.x-dev", - "dev-develop": "1.9.x-dev", - "dev-release-2.0": "2.0.x-dev" + "dev-release-1.8": "1.8.x-dev" } }, "autoload": { @@ -1376,7 +1425,7 @@ "psr", "psr-7" ], - "time": "2018-09-05T19:29:37+00:00" + "time": "2019-08-06T17:53:53+00:00" } ], "packages-dev": [ diff --git a/app/src/Controller/AppController.php b/app/src/Controller/AppController.php index ce508fd5f..39f112ccb 100644 --- a/app/src/Controller/AppController.php +++ b/app/src/Controller/AppController.php @@ -131,7 +131,7 @@ public function beforeRender(\Cake\Event\Event $event) { $this->set('vv_matchgrids', $this->Matchgrids->find('list')->find('activeMatchGrids')->order(['table_name' => 'ASC'])->toArray()); // The set of menu permissions, so the layout knows what to render - if($this->Authorization && $curUser) { + if(isset($this->Authorization) && $curUser) { // Ordinarily $this->Authorization will be set, but under certain error conditions // it won't, which will prevent error messages from rendering diff --git a/app/vendor/cakephp/cakephp/.mailmap b/app/vendor/cakephp/cakephp/.mailmap deleted file mode 100644 index 51b51af9d..000000000 --- a/app/vendor/cakephp/cakephp/.mailmap +++ /dev/null @@ -1,115 +0,0 @@ -Mark Story -Mark Story -Mark Story -José Lorenzo Rodríguez -José Lorenzo Rodríguez -José Lorenzo Rodríguez José Lorenzo Rodríguez Urdaneta -ADmad -Mathew Foscarini -Mathew Foscarini -Ceeram -Ceeram -Walther Lalk -Walther Lalk -Walther Lalk -Walther Lalk -Walther Lalk -Mark Scherer -Mark Scherer -Mark Scherer -phpnut -phpnut -AD7six -AD7six -predominant -mariano.iglesias -mariano.iglesias -antograssiot -Florian Krämer -Florian Krämer -Florian Krämer -Rachman Chavik -Rachman Chavik -jperras -renan.saddam -Ber Clausen -Marc Würth -Jad Bitar -Jad Bitar -Jad Bitar -Yves P -dogmatic69 -Majna -Robert Pustułka -Robert Pustułka -Robert Pustułka -Thomas Ploch -Tigran Gabrielyan -Bryan Crowe -Bryan Crowe -Sam -Jorge González -Saleh Souzanchi -Yevgeny Tomenko -Ricardo Arturo Cabral -Cauan Cabral -pirouet -davidsteinsland -jamiemill -Stefan Dickmann -Benjamin Tamási -Gordon Pettey (petteyg) -Mathieu de Ruiter -Cees-Jan Kiewiet -Fiblan -Haithem BEN GHORBAL -Pedro Perejón -Algirdas Gurevicius -Calin -Mikaël Capelle -OKINAKA Kenshin -Walter Nasich -mstra001 -Aymeric Derbois -Daniel -James Michael DuPont -James Michael DuPont -Jan Dorsman -Pierre Martin -Jeremy Harris -Jeremy Harris -Christian Winther -Christian Winther -Christian Winther -Jose Diaz-Gonzalez -Jose Diaz-Gonzalez -Jose Diaz-Gonzalez -Frank de Graaf -Frank de Graaf Frank de Graaf -Frank de Graaf -Marlin Cremers -Marlin Cremers -Marlin Cremers -David Yell -James Watts -Jonas Hartmann -Jonas Hartmann -Jonas Hartmann -Thom Seddon -Thom Seddon -Robbert Noordzij -Robbert Noordzij -Simon East -Simon East -Matt Alexander -Matt Alexander -Mark van Driel -Mark van Driel -Juan Basso -Juan Basso -antograssiot -antograssiot -Patrick Conroy -Patrick Conroy -saeideng -saeideng diff --git a/app/vendor/cakephp/cakephp/.varci.yml b/app/vendor/cakephp/cakephp/.varci.yml deleted file mode 100644 index d132e01e4..000000000 --- a/app/vendor/cakephp/cakephp/.varci.yml +++ /dev/null @@ -1,45 +0,0 @@ -ruleset: - label_defects: - name: "Label defects" - events: [ issues, pull_request ] - label: Defect - when: - - action = "opened" - - body matches "/\[\s?x\s?\] bug/" - - label_enhancements: - name: "Label enhancements" - events: [ issues, pull_request ] - label: Enhancement - when: - - action = "opened" - - body matches "/\[\s?x\s?\] enhancement/" - - label_rfcs: - name: "Label RFCs" - events: [ issues ] - label: RFC - when: - - action = "opened" - - body matches "/\[\s?x\s?\] feature\-discussion/" - - remove_invalid: - name: "Remove invalid tag when issue re-opened" - events: [ issues, pull_request ] - label: -Invalid - when: - - action = "reopened" - - filter(labels, "name") has "Invalid" - - request_missing_version: - name: "Request missing version" - events: [ issues ] - label: "On hold" - when: - - action = "opened" or action = "re-opened" - - body matches "/\[x\] bug/" - - 'not(body matches "/CakePHP Version: v?(\d+\.)?(\d+\.)?(\*|\d+)/")' - - 'not(body matches "/CakePHP Version: [0-9a-f]{5,40}/")' - - 'not(body matches "/cakephp\/cakephp\s+\d+\.\d+\.\d+/")' - comment: '{{ user.login }}, please include the CakePHP version number you are using in your description. It helps us debug your issue.' - diff --git a/app/vendor/cakephp/cakephp/LICENSE b/app/vendor/cakephp/cakephp/LICENSE index 90862ff0e..291d80e1b 100644 --- a/app/vendor/cakephp/cakephp/LICENSE +++ b/app/vendor/cakephp/cakephp/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2005-2018, Cake Software Foundation, Inc. (https://cakefoundation.org) +Copyright (c) 2005-present, Cake Software Foundation, Inc. (https://cakefoundation.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/app/vendor/cakephp/cakephp/README.md b/app/vendor/cakephp/cakephp/README.md index f452017be..53a03fcfc 100644 --- a/app/vendor/cakephp/cakephp/README.md +++ b/app/vendor/cakephp/cakephp/README.md @@ -38,7 +38,7 @@ recommend using the [app skeleton](https://github.com/cakephp/app) as a starting point. For existing applications you can run the following: ``` bash -$ composer require cakephp/cakephp:"~3.6" +$ composer require cakephp/cakephp ``` ## Running Tests diff --git a/app/vendor/cakephp/cakephp/SECURITY.md b/app/vendor/cakephp/cakephp/SECURITY.md new file mode 100644 index 000000000..ca634be2b --- /dev/null +++ b/app/vendor/cakephp/cakephp/SECURITY.md @@ -0,0 +1,30 @@ +# Security Policy + +## Supported Versions + +We support fixing security issues on the following releases: + +| Version | Supported | +| ------- | ------------------ | +| 3.7.x | :white_check_mark: | +| 3.6.x | :white_check_mark: | +| <= 3.5 | :x: | +| 2.10.x | :white_check_mark: | +| <= 2.9 | :x: | + +## Reporting a Vulnerability + +If you’ve found a security issue in CakePHP, please use the following procedure +instead of the normal bug reporting system. Instead of using the bug tracker, +mailing list or IRC please send an email to security [at] cakephp.org. Emails +sent to this address go to the CakePHP core team on a private mailing list. + +For each report, we try to first confirm the vulnerability. Once confirmed, +the CakePHP team will take the following actions: + +* Acknowledge to the reporter that we’ve received the issue, and are + working on a fix. We ask that the reporter keep the issue confidential until we announce it. +* Get a fix/patch prepared. +* Prepare a post describing the vulnerability, and the possible exploits. +* Release new versions of all affected versions. +* Prominently feature the problem in the release announcement diff --git a/app/vendor/cakephp/cakephp/VERSION.txt b/app/vendor/cakephp/cakephp/VERSION.txt index 905aba575..496abbcbe 100644 --- a/app/vendor/cakephp/cakephp/VERSION.txt +++ b/app/vendor/cakephp/cakephp/VERSION.txt @@ -16,4 +16,4 @@ // @license https://opensource.org/licenses/mit-license.php MIT License // +--------------------------------------------------------------------------------------------+ // //////////////////////////////////////////////////////////////////////////////////////////////////// -3.6.11 +3.8.3 diff --git a/app/vendor/cakephp/cakephp/composer.json b/app/vendor/cakephp/cakephp/composer.json index 8818ca52c..47e15a564 100644 --- a/app/vendor/cakephp/cakephp/composer.json +++ b/app/vendor/cakephp/cakephp/composer.json @@ -34,15 +34,18 @@ "cakephp/chronos": "^1.0.1", "aura/intl": "^3.0.0", "psr/log": "^1.0.0", + "psr/simple-cache": "^1.0.0", "zendframework/zend-diactoros": "^1.4.0" }, "suggest": { "ext-openssl": "To use Security::encrypt() or have secure CSRF token generation.", + "ext-curl": "To enable more efficient network calls in Http\\Client.", "lib-ICU": "The intl PHP library, to use Text::transliterate() or Text::slug()" }, "require-dev": { - "phpunit/phpunit": "^5.7.14|^6.0", - "cakephp/cakephp-codesniffer": "^3.0" + "cakephp/cakephp-codesniffer": "^3.0", + "cakephp/chronos": "^1.2.1", + "phpunit/phpunit": "^5.7.14|^6.0" }, "autoload": { "psr-4": { @@ -92,8 +95,8 @@ "@cs-check", "@test" ], - "cs-check": "phpcs --colors -p ./src ./tests", - "cs-fix": "phpcbf --colors ./src ./tests", + "cs-check": "phpcs --colors --parallel=16 -p src/ tests/", + "cs-fix": "phpcbf --colors --parallel=16 -p src/ tests/", "test": "phpunit", "test-coverage": "phpunit --coverage-clover=clover.xml" } diff --git a/app/vendor/cakephp/cakephp/config/bootstrap.php b/app/vendor/cakephp/cakephp/config/bootstrap.php index 1e40cbfde..c683c0df2 100644 --- a/app/vendor/cakephp/cakephp/config/bootstrap.php +++ b/app/vendor/cakephp/cakephp/config/bootstrap.php @@ -12,7 +12,6 @@ * @since 0.2.9 * @license https://opensource.org/licenses/mit-license.php MIT License */ - use Cake\Routing\Router; define('TIME_START', microtime(true)); diff --git a/app/vendor/cakephp/cakephp/config/cacert.pem b/app/vendor/cakephp/cakephp/config/cacert.pem index ee25bee11..09b4ce16b 100644 --- a/app/vendor/cakephp/cakephp/config/cacert.pem +++ b/app/vendor/cakephp/cakephp/config/cacert.pem @@ -1,7 +1,7 @@ ## ## Bundle of CA Root Certificates ## -## Certificate data from Mozilla as of: Wed Jun 20 03:12:06 2018 GMT +## Certificate data from Mozilla as of: Wed Jan 23 04:12:09 2019 GMT ## ## This is a bundle of X.509 certificates of public Certificate Authorities ## (CA). These were automatically extracted from Mozilla's root certificates @@ -14,7 +14,7 @@ ## Just configure this file as the SSLCACertificateFile. ## ## Conversion done with mk-ca-bundle.pl version 1.27. -## SHA256: c80f571d9f4ebca4a91e0ad3a546f263153d71afffc845c6f8f52ce9d1a2e8ec +## SHA256: 18372117493b5b7ec006c31d966143fc95a9464a2b5f8d5188e23c5557b2292d ## @@ -261,28 +261,6 @@ gn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS -----END CERTIFICATE----- -Visa eCommerce Root -=================== ------BEGIN CERTIFICATE----- -MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBrMQswCQYDVQQG -EwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2Ug -QXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2 -WhcNMjIwNjI0MDAxNjEyWjBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMm -VmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv -bW1lcmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h2mCxlCfL -F9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4ElpF7sDPwsRROEW+1QK8b -RaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdVZqW1LS7YgFmypw23RuwhY/81q6UCzyr0 -TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI -/k4+oKsGGelT84ATB+0tvz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzs -GHxBvfaLdXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG -MB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUFAAOCAQEAX/FBfXxc -CLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcRzCSs00Rsca4BIGsDoo8Ytyk6feUW -YFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pz -zkWKsKZJ/0x9nXGIxHYdkFsd7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBu -YQa7FkKMcPcw++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt -398znM/jra6O1I7mT1GvFpLgXPYHDw== ------END CERTIFICATE----- - Comodo AAA Services root ======================== -----BEGIN CERTIFICATE----- @@ -2792,126 +2770,6 @@ GiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7SofTUwJCA3sS61kFyjn dc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR -----END CERTIFICATE----- -Certplus Root CA G1 -=================== ------BEGIN CERTIFICATE----- -MIIFazCCA1OgAwIBAgISESBVg+QtPlRWhS2DN7cs3EYRMA0GCSqGSIb3DQEBDQUAMD4xCzAJBgNV -BAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMTAe -Fw0xNDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBaMD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhD -ZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMTCCAiIwDQYJKoZIhvcNAQEBBQAD -ggIPADCCAgoCggIBANpQh7bauKk+nWT6VjOaVj0W5QOVsjQcmm1iBdTYj+eJZJ+622SLZOZ5KmHN -r49aiZFluVj8tANfkT8tEBXgfs+8/H9DZ6itXjYj2JizTfNDnjl8KvzsiNWI7nC9hRYt6kuJPKNx -Qv4c/dMcLRC4hlTqQ7jbxofaqK6AJc96Jh2qkbBIb6613p7Y1/oA/caP0FG7Yn2ksYyy/yARujVj -BYZHYEMzkPZHogNPlk2dT8Hq6pyi/jQu3rfKG3akt62f6ajUeD94/vI4CTYd0hYCyOwqaK/1jpTv -LRN6HkJKHRUxrgwEV/xhc/MxVoYxgKDEEW4wduOU8F8ExKyHcomYxZ3MVwia9Az8fXoFOvpHgDm2 -z4QTd28n6v+WZxcIbekN1iNQMLAVdBM+5S//Ds3EC0pd8NgAM0lm66EYfFkuPSi5YXHLtaW6uOrc -4nBvCGrch2c0798wct3zyT8j/zXhviEpIDCB5BmlIOklynMxdCm+4kLV87ImZsdo/Rmz5yCTmehd -4F6H50boJZwKKSTUzViGUkAksnsPmBIgJPaQbEfIDbsYIC7Z/fyL8inqh3SV4EJQeIQEQWGw9CEj -jy3LKCHyamz0GqbFFLQ3ZU+V/YDI+HLlJWvEYLF7bY5KinPOWftwenMGE9nTdDckQQoRb5fc5+R+ -ob0V8rqHDz1oihYHAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0G -A1UdDgQWBBSowcCbkahDFXxdBie0KlHYlwuBsTAfBgNVHSMEGDAWgBSowcCbkahDFXxdBie0KlHY -lwuBsTANBgkqhkiG9w0BAQ0FAAOCAgEAnFZvAX7RvUz1isbwJh/k4DgYzDLDKTudQSk0YcbX8ACh -66Ryj5QXvBMsdbRX7gp8CXrc1cqh0DQT+Hern+X+2B50ioUHj3/MeXrKls3N/U/7/SMNkPX0XtPG -YX2eEeAC7gkE2Qfdpoq3DIMku4NQkv5gdRE+2J2winq14J2by5BSS7CTKtQ+FjPlnsZlFT5kOwQ/ -2wyPX1wdaR+v8+khjPPvl/aatxm2hHSco1S1cE5j2FddUyGbQJJD+tZ3VTNPZNX70Cxqjm0lpu+F -6ALEUz65noe8zDUa3qHpimOHZR4RKttjd5cUvpoUmRGywO6wT/gUITJDT5+rosuoD6o7BlXGEilX -CNQ314cnrUlZp5GrRHpejXDbl85IULFzk/bwg2D5zfHhMf1bfHEhYxQUqq/F3pN+aLHsIqKqkHWe -tUNy6mSjhEv9DKgma3GX7lZjZuhCVPnHHd/Qj1vfyDBviP4NxDMcU6ij/UgQ8uQKTuEVV/xuZDDC -VRHc6qnNSlSsKWNEz0pAoNZoWRsz+e86i9sgktxChL8Bq4fA1SCC28a5g4VCXA9DO2pJNdWY9BW/ -+mGBDAkgGNLQFwzLSABQ6XaCjGTXOqAHVcweMcDvOrRl++O/QmueD6i9a5jc2NvLi6Td11n0bt3+ -qsOR0C5CB8AMTVPNJLFMWx5R9N/pkvo= ------END CERTIFICATE----- - -Certplus Root CA G2 -=================== ------BEGIN CERTIFICATE----- -MIICHDCCAaKgAwIBAgISESDZkc6uo+jF5//pAq/Pc7xVMAoGCCqGSM49BAMDMD4xCzAJBgNVBAYT -AkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMjAeFw0x -NDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBaMD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0 -cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMjB2MBAGByqGSM49AgEGBSuBBAAiA2IA -BM0PW1aC3/BFGtat93nwHcmsltaeTpwftEIRyoa/bfuFo8XlGVzX7qY/aWfYeOKmycTbLXku54uN -Am8xIk0G42ByRZ0OQneezs/lf4WbGOT8zC5y0xaTTsqZY1yhBSpsBqNjMGEwDgYDVR0PAQH/BAQD -AgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNqDYwJ5jtpMxjwjFNiPwyCrKGBZMB8GA1Ud -IwQYMBaAFNqDYwJ5jtpMxjwjFNiPwyCrKGBZMAoGCCqGSM49BAMDA2gAMGUCMHD+sAvZ94OX7PNV -HdTcswYO/jOYnYs5kGuUIe22113WTNchp+e/IQ8rzfcq3IUHnQIxAIYUFuXcsGXCwI4Un78kFmjl -vPl5adytRSv3tjFzzAalU5ORGpOucGpnutee5WEaXw== ------END CERTIFICATE----- - -OpenTrust Root CA G1 -==================== ------BEGIN CERTIFICATE----- -MIIFbzCCA1egAwIBAgISESCzkFU5fX82bWTCp59rY45nMA0GCSqGSIb3DQEBCwUAMEAxCzAJBgNV -BAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEcx -MB4XDTE0MDUyNjA4NDU1MFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoM -CU9wZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzEwggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQD4eUbalsUwXopxAy1wpLuwxQjczeY1wICkES3d5oeuXT2R0odsN7fa -Yp6bwiTXj/HbpqbfRm9RpnHLPhsxZ2L3EVs0J9V5ToybWL0iEA1cJwzdMOWo010hOHQX/uMftk87 -ay3bfWAfjH1MBcLrARYVmBSO0ZB3Ij/swjm4eTrwSSTilZHcYTSSjFR077F9jAHiOH3BX2pfJLKO -YheteSCtqx234LSWSE9mQxAGFiQD4eCcjsZGT44ameGPuY4zbGneWK2gDqdkVBFpRGZPTBKnjix9 -xNRbxQA0MMHZmf4yzgeEtE7NCv82TWLxp2NX5Ntqp66/K7nJ5rInieV+mhxNaMbBGN4zK1FGSxyO -9z0M+Yo0FMT7MzUj8czxKselu7Cizv5Ta01BG2Yospb6p64KTrk5M0ScdMGTHPjgniQlQ/GbI4Kq -3ywgsNw2TgOzfALU5nsaqocTvz6hdLubDuHAk5/XpGbKuxs74zD0M1mKB3IDVedzagMxbm+WG+Oi -n6+Sx+31QrclTDsTBM8clq8cIqPQqwWyTBIjUtz9GVsnnB47ev1CI9sjgBPwvFEVVJSmdz7QdFG9 -URQIOTfLHzSpMJ1ShC5VkLG631UAC9hWLbFJSXKAqWLXwPYYEQRVzXR7z2FwefR7LFxckvzluFqr -TJOVoSfupb7PcSNCupt2LQIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB -/zAdBgNVHQ4EFgQUl0YhVyE12jZVx/PxN3DlCPaTKbYwHwYDVR0jBBgwFoAUl0YhVyE12jZVx/Px -N3DlCPaTKbYwDQYJKoZIhvcNAQELBQADggIBAB3dAmB84DWn5ph76kTOZ0BP8pNuZtQ5iSas000E -PLuHIT839HEl2ku6q5aCgZG27dmxpGWX4m9kWaSW7mDKHyP7Rbr/jyTwyqkxf3kfgLMtMrpkZ2Cv -uVnN35pJ06iCsfmYlIrM4LvgBBuZYLFGZdwIorJGnkSI6pN+VxbSFXJfLkur1J1juONI5f6ELlgK -n0Md/rcYkoZDSw6cMoYsYPXpSOqV7XAp8dUv/TW0V8/bhUiZucJvbI/NeJWsZCj9VrDDb8O+WVLh -X4SPgPL0DTatdrOjteFkdjpY3H1PXlZs5VVZV6Xf8YpmMIzUUmI4d7S+KNfKNsSbBfD4Fdvb8e80 -nR14SohWZ25g/4/Ii+GOvUKpMwpZQhISKvqxnUOOBZuZ2mKtVzazHbYNeS2WuOvyDEsMpZTGMKcm -GS3tTAZQMPH9WD25SxdfGbRqhFS0OE85og2WaMMolP3tLR9Ka0OWLpABEPs4poEL0L9109S5zvE/ -bw4cHjdx5RiHdRk/ULlepEU0rbDK5uUTdg8xFKmOLZTW1YVNcxVPS/KyPu1svf0OnWZzsD2097+o -4BGkxK51CUpjAEggpsadCwmKtODmzj7HPiY46SvepghJAwSQiumPv+i2tCqjI40cHLI5kqiPAlxA -OXXUc0ECd97N4EOH1uS6SsNsEn/+KuYj1oxx ------END CERTIFICATE----- - -OpenTrust Root CA G2 -==================== ------BEGIN CERTIFICATE----- -MIIFbzCCA1egAwIBAgISESChaRu/vbm9UpaPI+hIvyYRMA0GCSqGSIb3DQEBDQUAMEAxCzAJBgNV -BAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEcy -MB4XDTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoM -CU9wZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzIwggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQDMtlelM5QQgTJT32F+D3Y5z1zCU3UdSXqWON2ic2rxb95eolq5cSG+ -Ntmh/LzubKh8NBpxGuga2F8ORAbtp+Dz0mEL4DKiltE48MLaARf85KxP6O6JHnSrT78eCbY2albz -4e6WiWYkBuTNQjpK3eCasMSCRbP+yatcfD7J6xcvDH1urqWPyKwlCm/61UWY0jUJ9gNDlP7ZvyCV -eYCYitmJNbtRG6Q3ffyZO6v/v6wNj0OxmXsWEH4db0fEFY8ElggGQgT4hNYdvJGmQr5J1WqIP7wt -UdGejeBSzFfdNTVY27SPJIjki9/ca1TSgSuyzpJLHB9G+h3Ykst2Z7UJmQnlrBcUVXDGPKBWCgOz -3GIZ38i1MH/1PCZ1Eb3XG7OHngevZXHloM8apwkQHZOJZlvoPGIytbU6bumFAYueQ4xncyhZW+vj -3CzMpSZyYhK05pyDRPZRpOLAeiRXyg6lPzq1O4vldu5w5pLeFlwoW5cZJ5L+epJUzpM5ChaHvGOz -9bGTXOBut9Dq+WIyiET7vycotjCVXRIouZW+j1MY5aIYFuJWpLIsEPUdN6b4t/bQWVyJ98LVtZR0 -0dX+G7bw5tYee9I8y6jj9RjzIR9u701oBnstXW5DiabA+aC/gh7PU3+06yzbXfZqfUAkBXKJOAGT -y3HCOV0GEfZvePg3DTmEJwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB -/zAdBgNVHQ4EFgQUajn6QiL35okATV59M4PLuG53hq8wHwYDVR0jBBgwFoAUajn6QiL35okATV59 -M4PLuG53hq8wDQYJKoZIhvcNAQENBQADggIBAJjLq0A85TMCl38th6aP1F5Kr7ge57tx+4BkJamz -Gj5oXScmp7oq4fBXgwpkTx4idBvpkF/wrM//T2h6OKQQbA2xx6R3gBi2oihEdqc0nXGEL8pZ0keI -mUEiyTCYYW49qKgFbdEfwFFEVn8nNQLdXpgKQuswv42hm1GqO+qTRmTFAHneIWv2V6CG1wZy7HBG -S4tz3aAhdT7cHcCP009zHIXZ/n9iyJVvttN7jLpTwm+bREx50B1ws9efAvSyB7DH5fitIw6mVskp -EndI2S9G/Tvw/HRwkqWOOAgfZDC2t0v7NqwQjqBSM2OdAzVWxWm9xiNaJ5T2pBL4LTM8oValX9YZ -6e18CL13zSdkzJTaTkZQh+D5wVOAHrut+0dSixv9ovneDiK3PTNZbNTe9ZUGMg1RGUFcPk8G97kr -gCf2o6p6fAbhQ8MTOWIaNr3gKC6UAuQpLmBVrkA9sHSSXvAgZJY/X0VdiLWK2gKgW0VU3jg9CcCo -SmVGFvyqv1ROTVu+OEO3KMqLM6oaJbolXCkvW0pujOotnCr2BXbgd5eAiN1nE28daCSLT7d0geX0 -YJ96Vdc+N9oWaz53rK4YcJUIeSkDiv7BO7M/Gg+kO14fWKGVyasvc0rQLW6aWQ9VGHgtPFGml4vm -u7JwqkwR3v98KzfUetF3NI/n+UL3PIEMS1IK ------END CERTIFICATE----- - -OpenTrust Root CA G3 -==================== ------BEGIN CERTIFICATE----- -MIICITCCAaagAwIBAgISESDm+Ez8JLC+BUCs2oMbNGA/MAoGCCqGSM49BAMDMEAxCzAJBgNVBAYT -AkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEczMB4X -DTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9w -ZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzMwdjAQBgcqhkjOPQIBBgUrgQQA -IgNiAARK7liuTcpm3gY6oxH84Bjwbhy6LTAMidnW7ptzg6kjFYwvWYpa3RTqnVkrQ7cG7DK2uu5B -ta1doYXM6h0UZqNnfkbilPPntlahFVmhTzeXuSIevRHr9LIfXsMUmuXZl5mjYzBhMA4GA1UdDwEB -/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRHd8MUi2I5DMlv4VBN0BBY3JWIbTAf -BgNVHSMEGDAWgBRHd8MUi2I5DMlv4VBN0BBY3JWIbTAKBggqhkjOPQQDAwNpADBmAjEAj6jcnboM -BBf6Fek9LykBl7+BFjNAk2z8+e2AcG+qj9uEwov1NcoG3GRvaBbhj5G5AjEA2Euly8LQCGzpGPta -3U1fJAuwACEl74+nBCZx4nxp5V2a+EEfOzmTk51V6s2N8fvB ------END CERTIFICATE----- - ISRG Root X1 ============ -----BEGIN CERTIFICATE----- @@ -3312,3 +3170,232 @@ BBYEFFvKXuXe0oGqzagtZFG22XKbl+ZPMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe N+vp1RPZytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZgh5Mm m7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== -----END CERTIFICATE----- + +GlobalSign Root CA - R6 +======================= +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEgMB4GA1UECxMX +R2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds +b2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQxMjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9i +YWxTaWduIFJvb3QgQ0EgLSBSNjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFs +U2lnbjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQss +grRIxutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1kZguSgMpE +3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxDaNc9PIrFsmbVkJq3MQbF +vuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJwLnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqM +PKq0pPbzlUoSB239jLKJz9CgYXfIWHSw1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+ +azayOeSsJDa38O+2HBNXk7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05O +WgtH8wY2SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/hbguy +CLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4nWUx2OVvq+aWh2IMP +0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpYrZxCRXluDocZXFSxZba/jJvcE+kN +b7gu3GduyYsRtYQUigAZcIN5kZeR1BonvzceMgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQE +AwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNV +HSMEGDAWgBSubAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN +nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGtIxg93eFyRJa0 +lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr6155wsTLxDKZmOMNOsIeDjHfrY +BzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLjvUYAGm0CuiVdjaExUd1URhxN25mW7xocBFym +Fe944Hn+Xds+qkxV/ZoVqW/hpvvfcDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr +3TsTjxKM4kEaSHpzoHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB1 +0jZpnOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfspA9MRf/T +uTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+vJJUEeKgDu+6B5dpffItK +oZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+t +JDfLRVpOoERIyNiwmcUVhAn21klJwGW45hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GC CA +=============================== +-----BEGIN CERTIFICATE----- +MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQswCQYDVQQGEwJD +SDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEo +MCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRa +Fw00MjA1MDkwOTU4MzNaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQL +ExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh +bCBSb290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4nieUqjFqdr +VCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4Wp2OQ0jnUsYd4XxiWD1Ab +NTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7TrYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0E +AwMDaAAwZQIwJsdpW9zV57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtk +AjEA2zQgMgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 +-----END CERTIFICATE----- + +GTS Root R1 +=========== +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQbkepxUtHDA3sM9CJuRz04TANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQG +EwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJv +b3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAG +A1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx +9vaMf/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7wCl7r +aKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjwTcLCeoiKu7rPWRnW +r4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0PfyblqAj+lug8aJRT7oM6iCsVlgmy4HqM +LnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly +4cpk9+aCEI3oncKKiPo4Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr +06zqkUspzBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92 +wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70paDPvOmbsB4om +3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrNVjzRlwW5y0vtOUucxD/SVRNu +JLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEM +BQADggIBADiWCu49tJYeX++dnAsznyvgyv3SjgofQXSlfKqE1OXyHuY3UjKcC9FhHb8owbZEKTV1 +d5iyfNm9dKyKaOOpMQkpAWBz40d8U6iQSifvS9efk+eCNs6aaAyC58/UEBZvXw6ZXPYfcX3v73sv +fuo21pdwCxXu11xWajOl40k4DLh9+42FpLFZXvRq4d2h9mREruZRgyFmxhE+885H7pwoHyXa/6xm +ld01D1zvICxi/ZG6qcz8WpyTgYMpl0p8WnK0OdC3d8t5/Wk6kjftbjhlRn7pYL15iJdfOBL07q9b +gsiG1eGZbYwE8na6SfZu6W0eX6DvJ4J2QPim01hcDyxC2kLGe4g0x8HYRZvBPsVhHdljUEn2NIVq +4BjFbkerQUIpm/ZgDdIx02OYI5NaAIFItO/Nis3Jz5nu2Z6qNuFoS3FJFDYoOj0dzpqPJeaAcWEr +tXvM+SUWgeExX6GjfhaknBZqlxi9dnKlC54dNuYvoS++cJEPqOba+MSSQGwlfnuzCdyyF62ARPBo +pY+Udf90WuioAnwMCeKpSwughQtiue+hMZL77/ZRBIls6Kl0obsXs7X9SQ98POyDGCBDTtWTurQ0 +sR8WNh8M5mQ5Fkzc4P4dyKliPUDqysU0ArSuiYgzNdwsE3PYJ/HQcu51OyLemGhmW/HGY0dVHLql +CFF1pkgl +-----END CERTIFICATE----- + +GTS Root R2 +=========== +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQbkepxlqz5yDFMJo/aFLybzANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQG +EwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJv +b3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAG +A1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTuk +k3LvCvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY6Dlo +7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAuMC6C/Pq8tBcKSOWI +m8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7kRXuJVfeKH2JShBKzwkCX44ofR5Gm +dFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbu +ak7MkogwTZq9TwtImoS1mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscsz +cTJGr61K8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RW +Ir9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKaG73Vululycsl +aVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCqgc7dGtxRcw1PcOnlthYhGXmy +5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEM +BQADggIBALZp8KZ3/p7uC4Gt4cCpx/k1HUCCq+YEtN/L9x0Pg/B+E02NjO7jMyLDOfxA325BS0JT +vhaI8dI4XsRomRyYUpOM52jtG2pzegVATX9lO9ZY8c6DR2Dj/5epnGB3GFW1fgiTz9D2PGcDFWEJ ++YF59exTpJ/JjwGLc8R3dtyDovUMSRqodt6Sm2T4syzFJ9MHwAiApJiS4wGWAqoC7o87xdFtCjMw +c3i5T1QWvwsHoaRc5svJXISPD+AVdyx+Jn7axEvbpxZ3B7DNdehyQtaVhJ2Gg/LkkM0JR9SLA3Da +WsYDQvTtN6LwG1BUSw7YhN4ZKJmBR64JGz9I0cNv4rBgF/XuIwKl2gBbbZCr7qLpGzvpx0QnRY5r +n/WkhLx3+WuXrD5RRaIRpsyF7gpo8j5QOHokYh4XIDdtak23CZvJ/KRY9bb7nE4Yu5UC56Gtmwfu +Nmsk0jmGwZODUNKBRqhfYlcsu2xkiAhu7xNUX90txGdj08+JN7+dIPT7eoOboB6BAFDC5AwiWVIQ +7UNWhwD4FFKnHYuTjKJNRn8nxnGbJN7k2oaLDX5rIMHAnuFl2GqjpuiFizoHCBy69Y9Vmhh1fuXs +gWbRIXOhNUQLgD1bnF5vKheW0YMjiGZt5obicDIvUiLnyOd/xCxgXS/Dr55FBcOEArf9LAhST4Ld +o/DUhgkC +-----END CERTIFICATE----- + +GTS Root R3 +=========== +-----BEGIN CERTIFICATE----- +MIICDDCCAZGgAwIBAgIQbkepx2ypcyRAiQ8DVd2NHTAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJV +UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg +UjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE +ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUU +Rout736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL24Cej +QjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTB8Sa6oC2uhYHP +0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEAgFukfCPAlaUs3L6JbyO5o91lAFJekazInXJ0 +glMLfalAvWhgxeG4VDvBNhcl2MG9AjEAnjWSdIUlUfUk7GRSJFClH9voy8l27OyCbvWFGFPouOOa +KaqW04MjyaR7YbPMAuhd +-----END CERTIFICATE----- + +GTS Root R4 +=========== +-----BEGIN CERTIFICATE----- +MIICCjCCAZGgAwIBAgIQbkepyIuUtui7OyrYorLBmTAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJV +UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg +UjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE +ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa +6zzuhXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvRHYqj +QjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSATNbrdP9JNqPV +2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNnADBkAjBqUFJ0CMRw3J5QdCHojXohw0+WbhXRIjVhLfoI +N+4Zba3bssx9BzT1YBkstTTZbyACMANxsbqjYAuG7ZoIapVon+Kz4ZNkfF6Tpt95LY2F45TPI11x +zPKwTdb+mciUqXWi4w== +-----END CERTIFICATE----- + +UCA Global G2 Root +================== +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9MQswCQYDVQQG +EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBHbG9iYWwgRzIgUm9vdDAeFw0x +NjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0xCzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlU +cnVzdDEbMBkGA1UEAwwSVUNBIEdsb2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxeYrb3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmT +oni9kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzmVHqUwCoV +8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/RVogvGjqNO7uCEeBHANBS +h6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDcC/Vkw85DvG1xudLeJ1uK6NjGruFZfc8o +LTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIjtm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/ +R+zvWr9LesGtOxdQXGLYD0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBe +KW4bHAyvj5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6DlNaBa +4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6iIis7nCs+dwp4wwc +OxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznPO6Q0ibd5Ei9Hxeepl2n8pndntd97 +8XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFIHEjMz15DD/pQwIX4wVZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo +5sOASD0Ee/ojL3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5 +1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl1qnN3e92mI0A +Ds0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oUb3n09tDh05S60FdRvScFDcH9 +yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LVPtateJLbXDzz2K36uGt/xDYotgIVilQsnLAX +c47QN6MUPJiVAAwpBVueSUmxX8fjy88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHo +jhJi6IjMtX9Gl8CbEGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZk +bxqgDMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI+Vg7RE+x +ygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGyYiGqhkCyLmTTX8jjfhFn +RR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bXUB+K+wb1whnw0A== +-----END CERTIFICATE----- + +UCA Extended Validation Root +============================ +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQG +EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9u +IFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMxMDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8G +A1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrs +iWogD4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvSsPGP2KxF +Rv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aopO2z6+I9tTcg1367r3CTu +eUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dksHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR +59mzLC52LqGj3n5qiAno8geK+LLNEOfic0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH +0mK1lTnj8/FtDw5lhIpjVMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KR +el7sFsLzKuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/TuDv +B0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41Gsx2VYVdWf6/wFlth +WG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs1+lvK9JKBZP8nm9rZ/+I8U6laUpS +NwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQDfwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS +3H5aBZ8eNJr34RQwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQEL +BQADggIBADaNl8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR +ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQVBcZEhrxH9cM +aVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5c6sq1WnIeJEmMX3ixzDx/BR4 +dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb ++7lsq+KePRXBOy5nAliRn+/4Qh8st2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOW +F3sGPjLtx7dCvHaj2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwi +GpWOvpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2CxR9GUeOc +GMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmxcmtpzyKEC2IPrNkZAJSi +djzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbMfjKaiJUINlK73nZfdklJrX+9ZSCyycEr +dhh2n1ax +-----END CERTIFICATE----- + +Certigna Root CA +================ +-----BEGIN CERTIFICATE----- +MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAwWjELMAkGA1UE +BhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAwMiA0ODE0NjMwODEwMDAzNjEZ +MBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0xMzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjda +MFoxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYz +MDgxMDAwMzYxGTAXBgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sOty3tRQgX +stmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9MCiBtnyN6tMbaLOQdLNyz +KNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPuI9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8 +JXrJhFwLrN1CTivngqIkicuQstDuI7pmTLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16 +XdG+RCYyKfHx9WzMfgIhC59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq +4NYKpkDfePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3YzIoej +wpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWTCo/1VTp2lc5ZmIoJ +lXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1kJWumIWmbat10TWuXekG9qxf5kBdI +jzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp/ +/TBt2dzhauH8XwIDAQABo4IBGjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw +HQYDVR0OBBYEFBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of +1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczovL3d3d3cuY2Vy +dGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilodHRwOi8vY3JsLmNlcnRpZ25h +LmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYraHR0cDovL2NybC5kaGlteW90aXMuY29tL2Nl +cnRpZ25hcm9vdGNhLmNybDANBgkqhkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOIt +OoldaDgvUSILSo3L6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxP +TGRGHVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH60BGM+RFq +7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncBlA2c5uk5jR+mUYyZDDl3 +4bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdio2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd +8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS +6Cvu5zHbugRqh5jnxV/vfaci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaY +tlu3zM63Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayhjWZS +aX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw3kAP+HwV96LOPNde +E4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0= +-----END CERTIFICATE----- diff --git a/app/vendor/cakephp/cakephp/contrib/pre-commit b/app/vendor/cakephp/cakephp/contrib/pre-commit deleted file mode 100644 index 17332fc62..000000000 --- a/app/vendor/cakephp/cakephp/contrib/pre-commit +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/sh -FILES=`git diff --cached --name-only --diff-filter=ACMR HEAD | grep \\\\.php` -PROJECT=`php -r "echo dirname(dirname(realpath('$0')));"` - -# Determine if a file list is passed -if [ "$#" -eq 1 ] -then - oIFS=$IFS - IFS=' - ' - SFILES="$1" - IFS=$oIFS -fi -SFILES=${SFILES:-$FILES} - -echo "Checking PHP Lint..." -for FILE in $SFILES -do - php -l -d display_errors=0 $PROJECT/$FILE - if [ $? != 0 ] - then - echo "Fix the error before commit." - exit 1 - fi - FILES="$FILES $PROJECT/$FILE" -done - -if [ "$SFILES" != "" ] -then - echo "Running PHPCS" - ./vendor/bin/phpcs --standard=vendor/cakephp/cakephp-codesniffer/CakePHP $SFILES - if [ $? != 0 ] - then - echo "PHPCS Errors found; commit aborted." - exit 1 - fi -fi -exit $? diff --git a/app/vendor/cakephp/cakephp/phpcs.xml.dist b/app/vendor/cakephp/cakephp/phpcs.xml.dist deleted file mode 100644 index 9d4c21b75..000000000 --- a/app/vendor/cakephp/cakephp/phpcs.xml.dist +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - 0 - - diff --git a/app/vendor/cakephp/cakephp/phpstan.neon b/app/vendor/cakephp/cakephp/phpstan.neon deleted file mode 100644 index 00457a149..000000000 --- a/app/vendor/cakephp/cakephp/phpstan.neon +++ /dev/null @@ -1,39 +0,0 @@ -parameters: - autoload_files: - - tests/bootstrap.php - ignoreErrors: - - '#Function wincache_ucache_[a-zA-Z0-9_]+ not found#' - - '#Function xcache_[a-zA-Z0-9_]+ not found#' - - '#Cake\\Database\\Type\\[a-zA-Z0-9_]+Type::__construct\(\) does not call parent constructor from Cake\\Database\\Type#' - - '#Constructor of class Cake\\[a-zA-Z0-9_\\]+ has an unused parameter#' - - '#Access to undefined constant Memcached::OPT_CLIENT_MODE#' - - '#Access to undefined constant Memcached::DYNAMIC_CLIENT_MODE#' - - '#Access to undefined constant PDO::SQLSRV_ATTR_ENCODING#' - - '#Access to undefined constant PDO::SQLSRV_ENCODING_BINARY#' - - '#Constant XC_TYPE_VAR not found#' - - '#Access to an undefined property Psr\\Http\\Message\\UriInterface::\$webroot#' - - '#Access to an undefined property Psr\\Http\\Message\\UriInterface::\$base#' - - '#Result of method Cake\\Http\\Response::send\(\) \(void\) is used#' - - '#Method Cake\\View\\Form\\ContextInterface::val\(\) invoked with 2 parameters, 1 required#' - - '#Access to an undefined property Exception::\$queryString#' - - '#Access to an undefined property PHPUnit\\Framework\\Test::\$fixtureManager#' - - '#Method Redis::#' - - '#Call to an undefined method Traversable::getArrayCopy().#' - - '#Variable \$config in isset\(\) is never defined#' - - '#Call to static method id\(\) on an unknown class PHPUnit_Runner_Version#' - - '#Call to an undefined method DateTimeInterface::i18nFormat\(\)#' - - '#Cannot call method lastInsertId\(\) on null#' - - '#Call to an undefined method Cake\\Auth\\Storage\\StorageInterface::getConfig\(\)#' - - '#Call to an undefined method Cake\\Auth\\Storage\\StorageInterface::setConfig\(\)#' - - '#Variable \$_SESSION in isset\(\) always exists and is not nullable#' - - '#PHPDoc tag @throws with type PHPUnit\\Exception is not subtype of Throwable#' - earlyTerminatingMethodCalls: - Cake\Console\Shell: - - abort - -services: - - - class: Cake\PHPStan\AssociationTableMixinClassReflectionExtension - tags: - - phpstan.broker.methodsClassReflectionExtension - - phpstan.broker.propertiesClassReflectionExtension diff --git a/app/vendor/cakephp/cakephp/src/Auth/BaseAuthenticate.php b/app/vendor/cakephp/cakephp/src/Auth/BaseAuthenticate.php index acdde17dc..ec2a9ad6c 100644 --- a/app/vendor/cakephp/cakephp/src/Auth/BaseAuthenticate.php +++ b/app/vendor/cakephp/cakephp/src/Auth/BaseAuthenticate.php @@ -22,12 +22,9 @@ /** * Base Authentication class with common methods and properties. - * - * @mixin \Cake\Core\InstanceConfigTrait */ abstract class BaseAuthenticate implements EventListenerInterface { - use InstanceConfigTrait; use LocatorAwareTrait; @@ -142,8 +139,8 @@ protected function _findUser($username, $password = null) $result->unsetProperty($passwordField); } $hidden = $result->getHidden(); - if ($password === null && in_array($passwordField, $hidden)) { - $key = array_search($passwordField, $hidden); + if ($password === null && in_array($passwordField, $hidden, true)) { + $key = array_search($passwordField, $hidden, true); unset($hidden[$key]); $result->setHidden($hidden); } @@ -220,7 +217,7 @@ public function needsPasswordRehash() * * @param \Cake\Http\ServerRequest $request Request to get authentication information from. * @param \Cake\Http\Response $response A response object that can have headers added. - * @return mixed Either false on failure, or an array of user data on success. + * @return array|false Either false on failure, or an array of user data on success. */ abstract public function authenticate(ServerRequest $request, Response $response); @@ -229,7 +226,7 @@ abstract public function authenticate(ServerRequest $request, Response $response * systems like basic and digest auth. * * @param \Cake\Http\ServerRequest $request Request object. - * @return mixed Either false or an array of user information + * @return array|false Either false or an array of user information */ public function getUser(ServerRequest $request) { diff --git a/app/vendor/cakephp/cakephp/src/Auth/BaseAuthorize.php b/app/vendor/cakephp/cakephp/src/Auth/BaseAuthorize.php index d03092479..0f3bcfaa9 100644 --- a/app/vendor/cakephp/cakephp/src/Auth/BaseAuthorize.php +++ b/app/vendor/cakephp/cakephp/src/Auth/BaseAuthorize.php @@ -22,11 +22,9 @@ * Abstract base authorization adapter for AuthComponent. * * @see \Cake\Controller\Component\AuthComponent::$authenticate - * @mixin \Cake\Core\InstanceConfigTrait */ abstract class BaseAuthorize { - use InstanceConfigTrait; /** diff --git a/app/vendor/cakephp/cakephp/src/Auth/BasicAuthenticate.php b/app/vendor/cakephp/cakephp/src/Auth/BasicAuthenticate.php index 0108c745f..f512e0942 100644 --- a/app/vendor/cakephp/cakephp/src/Auth/BasicAuthenticate.php +++ b/app/vendor/cakephp/cakephp/src/Auth/BasicAuthenticate.php @@ -58,7 +58,7 @@ class BasicAuthenticate extends BaseAuthenticate * * @param \Cake\Http\ServerRequest $request The request to authenticate with. * @param \Cake\Http\Response $response The response to add headers to. - * @return mixed Either false on failure, or an array of user data on success. + * @return array|false Either false on failure, or an array of user data on success. */ public function authenticate(ServerRequest $request, Response $response) { @@ -69,7 +69,7 @@ public function authenticate(ServerRequest $request, Response $response) * Get a user based on information in the request. Used by cookie-less auth for stateless clients. * * @param \Cake\Http\ServerRequest $request Request object. - * @return mixed Either false or an array of user information + * @return array|false Either false or an array of user information */ public function getUser(ServerRequest $request) { diff --git a/app/vendor/cakephp/cakephp/src/Auth/DigestAuthenticate.php b/app/vendor/cakephp/cakephp/src/Auth/DigestAuthenticate.php index 327491f73..546315b10 100644 --- a/app/vendor/cakephp/cakephp/src/Auth/DigestAuthenticate.php +++ b/app/vendor/cakephp/cakephp/src/Auth/DigestAuthenticate.php @@ -100,7 +100,7 @@ public function __construct(ComponentRegistry $registry, array $config = []) * Get a user based on information in the request. Used by cookie-less auth for stateless clients. * * @param \Cake\Http\ServerRequest $request Request object. - * @return mixed Either false or an array of user information + * @return array|false Either false or an array of user information */ public function getUser(ServerRequest $request) { diff --git a/app/vendor/cakephp/cakephp/src/Auth/FormAuthenticate.php b/app/vendor/cakephp/cakephp/src/Auth/FormAuthenticate.php index abd78f90a..a16decd77 100644 --- a/app/vendor/cakephp/cakephp/src/Auth/FormAuthenticate.php +++ b/app/vendor/cakephp/cakephp/src/Auth/FormAuthenticate.php @@ -73,7 +73,7 @@ protected function _checkFields(ServerRequest $request, array $fields) * * @param \Cake\Http\ServerRequest $request The request that contains login information. * @param \Cake\Http\Response $response Unused response object. - * @return mixed False on login failure. An array of User data on success. + * @return array|false False on login failure. An array of User data on success. */ public function authenticate(ServerRequest $request, Response $response) { diff --git a/app/vendor/cakephp/cakephp/src/Auth/Storage/MemoryStorage.php b/app/vendor/cakephp/cakephp/src/Auth/Storage/MemoryStorage.php index 999d43ca3..f758569be 100644 --- a/app/vendor/cakephp/cakephp/src/Auth/Storage/MemoryStorage.php +++ b/app/vendor/cakephp/cakephp/src/Auth/Storage/MemoryStorage.php @@ -28,9 +28,9 @@ class MemoryStorage implements StorageInterface protected $_user; /** - * Redirect url. + * Redirect URL. * - * @var string|null + * @var string|array|null */ protected $_redirectUrl; diff --git a/app/vendor/cakephp/cakephp/src/Auth/Storage/SessionStorage.php b/app/vendor/cakephp/cakephp/src/Auth/Storage/SessionStorage.php index 749e88ebb..7751f163b 100644 --- a/app/vendor/cakephp/cakephp/src/Auth/Storage/SessionStorage.php +++ b/app/vendor/cakephp/cakephp/src/Auth/Storage/SessionStorage.php @@ -20,12 +20,9 @@ /** * Session based persistent storage for authenticated user record. - * - * @mixin \Cake\Core\InstanceConfigTrait */ class SessionStorage implements StorageInterface { - use InstanceConfigTrait; /** diff --git a/app/vendor/cakephp/cakephp/src/Auth/Storage/StorageInterface.php b/app/vendor/cakephp/cakephp/src/Auth/Storage/StorageInterface.php index 5ed9d512c..53c3ebe9e 100644 --- a/app/vendor/cakephp/cakephp/src/Auth/Storage/StorageInterface.php +++ b/app/vendor/cakephp/cakephp/src/Auth/Storage/StorageInterface.php @@ -47,7 +47,7 @@ public function delete(); * * @param mixed $url Redirect URL. If `null` returns current URL. If `false` * deletes currently set URL. - * @return mixed + * @return string|array|null */ public function redirectUrl($url = null); } diff --git a/app/vendor/cakephp/cakephp/src/Cache/Cache.php b/app/vendor/cakephp/cakephp/src/Cache/Cache.php index 7b74c5f0c..c4a89e060 100644 --- a/app/vendor/cakephp/cakephp/src/Cache/Cache.php +++ b/app/vendor/cakephp/cakephp/src/Cache/Cache.php @@ -44,18 +44,20 @@ * In general all Cache operations are supported by all cache engines. * However, Cache::increment() and Cache::decrement() are not supported by File caching. * - * There are 6 built-in caching engines: + * There are 7 built-in caching engines: * + * - `ApcuEngine` - Uses the APCu object cache, one of the fastest caching engines. + * - `ArrayEngine` - Uses only memory to store all data, not actually a persistent engine. + * Can be useful in test or CLI environment. * - `FileEngine` - Uses simple files to store content. Poor performance, but good for * storing large objects, or things that are not IO sensitive. Well suited to development * as it is an easy cache to inspect and manually flush. - * - `ApcuEngine` - Uses the APCu object cache, one of the fastest caching engines. * - `MemcacheEngine` - Uses the PECL::Memcache extension and Memcached for storage. * Fast reads/writes, and benefits from memcache being distributed. - * - `XcacheEngine` - Uses the Xcache extension, an alternative to APCu. + * - `RedisEngine` - Uses redis and php-redis extension to store cache data. * - `WincacheEngine` - Uses Windows Cache Extension for PHP. Supports wincache 1.1.0 and higher. * This engine is recommended to people deploying on windows with IIS. - * - `RedisEngine` - Uses redis and php-redis extension to store cache data. + * - `XcacheEngine` - Uses the Xcache extension, an alternative to APCu. * * See Cache engine documentation for expected configuration keys. * @@ -73,6 +75,7 @@ class Cache * @var array */ protected static $_dsnClassMap = [ + 'array' => 'Cake\Cache\Engine\ArrayEngine', 'apc' => 'Cake\Cache\Engine\ApcuEngine', // @deprecated Since 3.6. Use apcu instead. 'apcu' => 'Cake\Cache\Engine\ApcuEngine', 'file' => 'Cake\Cache\Engine\FileEngine', @@ -216,6 +219,8 @@ protected static function _buildEngine($name) * * @param string $config The configuration name you want an engine for. * @return \Cake\Cache\CacheEngine When caching is disabled a null engine will be returned. + * @deprecated 3.7.0 Use Cache::pool() instead. In 4.0 all cache engines will implement the + * PSR16 interface and this method does not return objects implementing that interface. */ public static function engine($config) { @@ -234,6 +239,17 @@ public static function engine($config) return $registry->{$config}; } + /** + * Get a SimpleCacheEngine object for the named cache pool. + * + * @param string $config The name of the configured cache backend. + * @return \Cake\Cache\SimpleCacheEngine + */ + public static function pool($config) + { + return new SimpleCacheEngine(static::engine($config)); + } + /** * Garbage collection * @@ -242,6 +258,7 @@ public static function engine($config) * @param string $config [optional] The config name you wish to have garbage collected. Defaults to 'default' * @param int|null $expires [optional] An expires timestamp. Defaults to NULL * @return void + * @deprecated 3.7.0 Will be removed in 4.0 */ public static function gc($config = 'default', $expires = null) { @@ -273,19 +290,19 @@ public static function gc($config = 'default', $expires = null) */ public static function write($key, $value, $config = 'default') { - $engine = static::engine($config); if (is_resource($value)) { return false; } - $success = $engine->write($key, $value); + $backend = static::pool($config); + $success = $backend->set($key, $value); if ($success === false && $value !== '') { trigger_error( sprintf( "%s cache was unable to write '%s' to %s cache", $config, $key, - get_class($engine) + get_class($backend) ), E_USER_WARNING ); @@ -319,6 +336,7 @@ public static function write($key, $value, $config = 'default') public static function writeMany($data, $config = 'default') { $engine = static::engine($config); + $return = $engine->writeMany($data); foreach ($return as $key => $success) { if ($success === false && $data[$key] !== '') { @@ -357,6 +375,7 @@ public static function writeMany($data, $config = 'default') */ public static function read($key, $config = 'default') { + // TODO In 4.x this needs to change to use pool() $engine = static::engine($config); return $engine->read($key); @@ -386,6 +405,7 @@ public static function read($key, $config = 'default') */ public static function readMany($keys, $config = 'default') { + // In 4.x this needs to change to use pool() $engine = static::engine($config); return $engine->readMany($keys); @@ -397,12 +417,12 @@ public static function readMany($keys, $config = 'default') * @param string $key Identifier for the data * @param int $offset How much to add * @param string $config Optional string configuration name. Defaults to 'default' - * @return mixed new value, or false if the data doesn't exist, is not integer, + * @return int|false New value, or false if the data doesn't exist, is not integer, * or if there was an error fetching it. */ public static function increment($key, $offset = 1, $config = 'default') { - $engine = static::engine($config); + $engine = static::pool($config); if (!is_int($offset) || $offset < 0) { return false; } @@ -416,12 +436,12 @@ public static function increment($key, $offset = 1, $config = 'default') * @param string $key Identifier for the data * @param int $offset How much to subtract * @param string $config Optional string configuration name. Defaults to 'default' - * @return mixed new value, or false if the data doesn't exist, is not integer, + * @return int|false New value, or false if the data doesn't exist, is not integer, * or if there was an error fetching it */ public static function decrement($key, $offset = 1, $config = 'default') { - $engine = static::engine($config); + $engine = static::pool($config); if (!is_int($offset) || $offset < 0) { return false; } @@ -452,9 +472,9 @@ public static function decrement($key, $offset = 1, $config = 'default') */ public static function delete($key, $config = 'default') { - $engine = static::engine($config); + $backend = static::pool($config); - return $engine->delete($key); + return $backend->delete($key); } /** @@ -476,20 +496,26 @@ public static function delete($key, $config = 'default') * * @param array $keys Array of cache keys to be deleted * @param string $config name of the configuration to use. Defaults to 'default' - * @return array of boolean values that are true if the value was successfully deleted, false if it didn't exist or - * couldn't be removed + * @return array of boolean values that are true if the value was successfully deleted, + * false if it didn't exist or couldn't be removed. */ public static function deleteMany($keys, $config = 'default') { - $engine = static::engine($config); + $backend = static::pool($config); + + $return = []; + foreach ($keys as $key) { + $return[$key] = $backend->delete($key); + } - return $engine->deleteMany($keys); + return $return; } /** * Delete all keys from the cache. * - * @param bool $check if true will check expiration, otherwise delete all + * @param bool $check if true will check expiration, otherwise delete all. This parameter + * will become a no-op value in 4.0 as it is deprecated. * @param string $config name of the configuration to use. Defaults to 'default' * @return bool True if the cache was successfully cleared, false otherwise */ @@ -503,7 +529,8 @@ public static function clear($check = false, $config = 'default') /** * Delete all keys from the cache from all configurations. * - * @param bool $check if true will check expiration, otherwise delete all + * @param bool $check if true will check expiration, otherwise delete all. This parameter + * will become a no-op value in 4.0 as it is deprecated. * @return array Status code. For each configuration, it reports the status of the operation */ public static function clearAll($check = false) @@ -526,7 +553,7 @@ public static function clearAll($check = false) */ public static function clearGroup($group, $config = 'default') { - $engine = static::engine($config); + $engine = static::pool($config); return $engine->clearGroup($group); } @@ -660,11 +687,11 @@ public static function remember($key, $callable, $config = 'default') */ public static function add($key, $value, $config = 'default') { - $engine = static::engine($config); + $pool = static::pool($config); if (is_resource($value)) { return false; } - return $engine->add($key, $value); + return $pool->add($key, $value); } } diff --git a/app/vendor/cakephp/cakephp/src/Cache/CacheEngine.php b/app/vendor/cakephp/cakephp/src/Cache/CacheEngine.php index 32658f09f..e1bdc75a0 100644 --- a/app/vendor/cakephp/cakephp/src/Cache/CacheEngine.php +++ b/app/vendor/cakephp/cakephp/src/Cache/CacheEngine.php @@ -19,12 +19,9 @@ /** * Storage engine for CakePHP caching - * - * @mixin \Cake\Core\InstanceConfigTrait */ abstract class CacheEngine { - use InstanceConfigTrait; /** diff --git a/app/vendor/cakephp/cakephp/src/Cache/CacheEngineInterface.php b/app/vendor/cakephp/cakephp/src/Cache/CacheEngineInterface.php new file mode 100644 index 000000000..0b8cac694 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Cache/CacheEngineInterface.php @@ -0,0 +1,67 @@ + [exp => expiration, val => value]] + * + * @var array + */ + protected $data = []; + + /** + * Write data for key into cache + * + * @param string $key Identifier for the data + * @param mixed $value Data to be cached + * @return bool True if the data was successfully cached, false on failure + */ + public function write($key, $value) + { + $key = $this->_key($key); + $expires = time() + $this->_config['duration']; + $this->data[$key] = ['exp' => $expires, 'val' => $value]; + + return true; + } + + /** + * Read a key from the cache + * + * @param string $key Identifier for the data + * @return mixed The cached data, or false if the data doesn't exist, + * has expired, or if there was an error fetching it + */ + public function read($key) + { + $key = $this->_key($key); + if (!isset($this->data[$key])) { + return false; + } + $data = $this->data[$key]; + + // Check expiration + $now = time(); + if ($data['exp'] <= $now) { + unset($this->data[$key]); + + return false; + } + + return $data['val']; + } + + /** + * Increments the value of an integer cached key + * + * @param string $key Identifier for the data + * @param int $offset How much to increment + * @return bool|int New incremented value, false otherwise + */ + public function increment($key, $offset = 1) + { + if (!$this->read($key)) { + $this->write($key, 0); + } + $key = $this->_key($key); + $this->data[$key]['val'] += $offset; + + return $this->data[$key]['val']; + } + + /** + * Decrements the value of an integer cached key + * + * @param string $key Identifier for the data + * @param int $offset How much to subtract + * @return bool|int New decremented value, false otherwise + */ + public function decrement($key, $offset = 1) + { + if (!$this->read($key)) { + $this->write($key, 0); + } + $key = $this->_key($key); + $this->data[$key]['val'] -= $offset; + + return $this->data[$key]['val']; + } + + /** + * Delete a key from the cache + * + * @param string $key Identifier for the data + * @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed + */ + public function delete($key) + { + $key = $this->_key($key); + unset($this->data[$key]); + + return true; + } + + /** + * Delete all keys from the cache. This will clear every cache config using APC. + * + * @param bool $check Unused argument required by interface. + * @return bool True Returns true. + */ + public function clear($check) + { + $this->data = []; + + return true; + } + + /** + * Returns the `group value` for each of the configured groups + * If the group initial value was not found, then it initializes + * the group accordingly. + * + * @return array + */ + public function groups() + { + $result = []; + foreach ($this->_config['groups'] as $group) { + $key = $this->_config['prefix'] . $group; + if (!isset($this->data[$key])) { + $this->data[$key] = ['exp' => PHP_INT_MAX, 'val' => 1]; + } + $value = $this->data[$key]['val']; + $result[] = $group . $value; + } + + return $result; + } + + /** + * Increments the group value to simulate deletion of all keys under a group + * old values will remain in storage until they expire. + * + * @param string $group The group to clear. + * @return bool success + */ + public function clearGroup($group) + { + $key = $this->_config['prefix'] . $group; + if (isset($this->data[$key])) { + $this->data[$key]['val'] += 1; + } + + return true; + } +} diff --git a/app/vendor/cakephp/cakephp/src/Cache/Engine/FileEngine.php b/app/vendor/cakephp/cakephp/src/Cache/Engine/FileEngine.php index 49247cc68..108b7ce78 100644 --- a/app/vendor/cakephp/cakephp/src/Cache/Engine/FileEngine.php +++ b/app/vendor/cakephp/cakephp/src/Cache/Engine/FileEngine.php @@ -16,6 +16,7 @@ use Cake\Cache\CacheEngine; use Cake\Utility\Inflector; +use CallbackFilterIterator; use Exception; use LogicException; use RecursiveDirectoryIterator; @@ -193,7 +194,7 @@ public function read($key) $time = time(); $cachetime = (int)$this->_File->current(); - if ($cachetime < $time || ($time + $this->_config['duration']) < $cachetime) { + if ($cachetime < $time) { if ($this->_config['lock']) { $this->_File->flock(LOCK_UN); } @@ -268,7 +269,10 @@ public function clear($check) $this->_clearDirectory($this->_config['path'], $now, $threshold); - $directory = new RecursiveDirectoryIterator($this->_config['path']); + $directory = new RecursiveDirectoryIterator( + $this->_config['path'], + \FilesystemIterator::SKIP_DOTS + ); $contents = new RecursiveIteratorIterator( $directory, RecursiveIteratorIterator::SELF_FIRST @@ -280,7 +284,7 @@ public function clear($check) } $path = $path->getRealPath() . DIRECTORY_SEPARATOR; - if (!in_array($path, $cleared)) { + if (!in_array($path, $cleared, true)) { $this->_clearDirectory($path, $now, $threshold); $cleared[] = $path; } @@ -336,6 +340,8 @@ protected function _clearDirectory($path, $now, $threshold) //@codingStandardsIgnoreEnd } } + + $dir->close(); } /** @@ -389,7 +395,10 @@ protected function _setKey($key, $createKey = false) if (!$createKey && !$path->isFile()) { return false; } - if (empty($this->_File) || $this->_File->getBasename() !== $key) { + if (empty($this->_File) || + $this->_File->getBasename() !== $key || + $this->_File->valid() === false + ) { $exists = file_exists($path->getPathname()); try { $this->_File = $path->openFile('c+'); @@ -470,24 +479,40 @@ public function key($key) public function clearGroup($group) { $this->_File = null; + + $prefix = (string)$this->_config['prefix']; + $directoryIterator = new RecursiveDirectoryIterator($this->_config['path']); $contents = new RecursiveIteratorIterator( $directoryIterator, RecursiveIteratorIterator::CHILD_FIRST ); - foreach ($contents as $object) { - $containsGroup = strpos($object->getPathname(), DIRECTORY_SEPARATOR . $group . DIRECTORY_SEPARATOR) !== false; - $hasPrefix = true; - if (strlen($this->_config['prefix']) !== 0) { - $hasPrefix = strpos($object->getBasename(), $this->_config['prefix']) === 0; - } - if ($object->isFile() && $containsGroup && $hasPrefix) { - $path = $object->getPathname(); - $object = null; - //@codingStandardsIgnoreStart - @unlink($path); - //@codingStandardsIgnoreEnd + $filtered = new CallbackFilterIterator( + $contents, + function (SplFileInfo $current) use ($group, $prefix) { + if (!$current->isFile()) { + return false; + } + + $hasPrefix = $prefix === '' + || strpos($current->getBasename(), $prefix) === 0; + if ($hasPrefix === false) { + return false; + } + + $pos = strpos( + $current->getPathname(), + DIRECTORY_SEPARATOR . $group . DIRECTORY_SEPARATOR + ); + + return $pos !== false; } + ); + foreach ($filtered as $object) { + $path = $object->getPathname(); + $object = null; + // @codingStandardsIgnoreLine + @unlink($path); } return true; diff --git a/app/vendor/cakephp/cakephp/src/Cache/Engine/MemcachedEngine.php b/app/vendor/cakephp/cakephp/src/Cache/Engine/MemcachedEngine.php index 8e6b1c56e..3943e13a1 100644 --- a/app/vendor/cakephp/cakephp/src/Cache/Engine/MemcachedEngine.php +++ b/app/vendor/cakephp/cakephp/src/Cache/Engine/MemcachedEngine.php @@ -23,10 +23,9 @@ * control you have over expire times far in the future. See MemcachedEngine::write() for * more information. * - * Main advantage of this Memcached engine over the memcached engine is - * support of binary protocol, and igbinary serialization - * (if memcached extension compiled with --enable-igbinary) - * Compressed keys can also be incremented/decremented + * Memcached engine supports binary protocol and igbinary + * serialization (if memcached extension is compiled with --enable-igbinary). + * Compressed keys can also be incremented/decremented. */ class MemcachedEngine extends CacheEngine { diff --git a/app/vendor/cakephp/cakephp/src/Cache/Engine/NullEngine.php b/app/vendor/cakephp/cakephp/src/Cache/Engine/NullEngine.php index 83ba3cde2..fee563583 100644 --- a/app/vendor/cakephp/cakephp/src/Cache/Engine/NullEngine.php +++ b/app/vendor/cakephp/cakephp/src/Cache/Engine/NullEngine.php @@ -17,7 +17,7 @@ use Cake\Cache\CacheEngine; /** - * Null cache engine, all operations return false. + * Null cache engine, all operations appear to work, but do nothing. * * This is used internally for when Cache::disable() has been called. */ @@ -44,13 +44,7 @@ public function gc($expires = null) */ public function write($key, $value) { - } - - /** - * {@inheritDoc} - */ - public function writeMany($data) - { + return true; } /** @@ -74,6 +68,7 @@ public function readMany($keys) */ public function increment($key, $offset = 1) { + return true; } /** @@ -81,6 +76,7 @@ public function increment($key, $offset = 1) */ public function decrement($key, $offset = 1) { + return true; } /** @@ -88,6 +84,7 @@ public function decrement($key, $offset = 1) */ public function delete($key) { + return true; } /** diff --git a/app/vendor/cakephp/cakephp/src/Cache/Engine/RedisEngine.php b/app/vendor/cakephp/cakephp/src/Cache/Engine/RedisEngine.php index 1890118a5..245db502b 100644 --- a/app/vendor/cakephp/cakephp/src/Cache/Engine/RedisEngine.php +++ b/app/vendor/cakephp/cakephp/src/Cache/Engine/RedisEngine.php @@ -140,7 +140,7 @@ public function write($key, $value) return $this->_Redis->set($key, $value); } - return $this->_Redis->setex($key, $duration, $value); + return $this->_Redis->setEx($key, $duration, $value); } /** @@ -178,7 +178,7 @@ public function increment($key, $offset = 1) $value = (int)$this->_Redis->incrBy($key, $offset); if ($duration > 0) { - $this->_Redis->setTimeout($key, $duration); + $this->_Redis->expire($key, $duration); } return $value; @@ -198,7 +198,7 @@ public function decrement($key, $offset = 1) $value = (int)$this->_Redis->decrBy($key, $offset); if ($duration > 0) { - $this->_Redis->setTimeout($key, $duration); + $this->_Redis->expire($key, $duration); } return $value; @@ -214,7 +214,7 @@ public function delete($key) { $key = $this->_key($key); - return $this->_Redis->delete($key) > 0; + return $this->_Redis->del($key) > 0; } /** @@ -228,14 +228,27 @@ public function clear($check) if ($check) { return true; } - $keys = $this->_Redis->getKeys($this->_config['prefix'] . '*'); - $result = []; - foreach ($keys as $key) { - $result[] = $this->_Redis->delete($key) > 0; + $this->_Redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); + + $isAllDeleted = true; + $iterator = null; + $pattern = $this->_config['prefix'] . '*'; + + while (true) { + $keys = $this->_Redis->scan($iterator, $pattern); + + if ($keys === false) { + break; + } + + foreach ($keys as $key) { + $isDeleted = ($this->_Redis->del($key) > 0); + $isAllDeleted = $isAllDeleted && $isDeleted; + } } - return !in_array(false, $result); + return $isAllDeleted; } /** @@ -256,9 +269,9 @@ public function add($key, $value) $value = serialize($value); } - // setnx() doesn't have an expiry option, so follow up with an expiry - if ($this->_Redis->setnx($key, $value)) { - return $this->_Redis->setTimeout($key, $duration); + // setNx() doesn't have an expiry option, so follow up with an expiry + if ($this->_Redis->setNx($key, $value)) { + return $this->_Redis->expire($key, $duration); } return false; diff --git a/app/vendor/cakephp/cakephp/src/Cache/InvalidArgumentException.php b/app/vendor/cakephp/cakephp/src/Cache/InvalidArgumentException.php new file mode 100644 index 000000000..fefabd750 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Cache/InvalidArgumentException.php @@ -0,0 +1,25 @@ +innerEngine = $innerEngine; + } + + /** + * Ensure the validity of the given cache key. + * + * @param string $key Key to check. + * @return void + * @throws \Cake\Cache\InvalidArgumentException When the key is not valid. + */ + protected function ensureValidKey($key) + { + if (!is_string($key) || strlen($key) === 0) { + throw new InvalidArgumentException('A cache key must be a non-empty string.'); + } + } + + /** + * Ensure the validity of the given cache keys. + * + * @param mixed $keys The keys to check. + * @return void + * @throws \Cake\Cache\InvalidArgumentException When the keys are not valid. + */ + protected function ensureValidKeys($keys) + { + if (!is_array($keys) && !($keys instanceof \Traversable)) { + throw new InvalidArgumentException('A cache key set must be either an array or a Traversable.'); + } + + foreach ($keys as $key) { + $this->ensureValidKey($key); + } + } + + /** + * Fetches the value for a given key from the cache. + * + * @param string $key The unique key of this item in the cache. + * @param mixed $default Default value to return if the key does not exist. + * @return mixed The value of the item from the cache, or $default in case of cache miss. + * @throws \Cake\Cache\InvalidArgumentException If the $key string is not a legal value. + */ + public function get($key, $default = null) + { + $this->ensureValidKey($key); + $result = $this->innerEngine->read($key); + if ($result === false) { + return $default; + } + + return $result; + } + + /** + * Persists data in the cache, uniquely referenced by the given key with an optional expiration TTL time. + * + * @param string $key The key of the item to store. + * @param mixed $value The value of the item to store, must be serializable. + * @param null|int|\DateInterval $ttl Optional. The TTL value of this item. If no value is sent and + * the driver supports TTL then the library may set a default value + * for it or let the driver take care of that. + * @return bool True on success and false on failure. + * @throws \Cake\Cache\InvalidArgumentException + * MUST be thrown if the $key string is not a legal value. + */ + public function set($key, $value, $ttl = null) + { + $this->ensureValidKey($key); + if ($ttl !== null) { + $restore = $this->innerEngine->getConfig('duration'); + $this->innerEngine->setConfig('duration', $ttl); + } + try { + $result = $this->innerEngine->write($key, $value); + + return (bool)$result; + } finally { + if (isset($restore)) { + $this->innerEngine->setConfig('duration', $restore); + } + } + } + + /** + * Delete an item from the cache by its unique key. + * + * @param string $key The unique cache key of the item to delete. + * @return bool True if the item was successfully removed. False if there was an error. + * @throws \Cake\Cache\InvalidArgumentException If the $key string is not a legal value. + */ + public function delete($key) + { + $this->ensureValidKey($key); + + return $this->innerEngine->delete($key); + } + + /** + * Wipes clean the entire cache's keys. + * + * @return bool True on success and false on failure. + */ + public function clear() + { + return $this->innerEngine->clear(false); + } + + /** + * Obtains multiple cache items by their unique keys. + * + * @param iterable $keys A list of keys that can obtained in a single operation. + * @param mixed $default Default value to return for keys that do not exist. + * @return iterable A list of key => value pairs. Cache keys that do not exist or are stale will have $default as value. + * @throws \Cake\Cache\InvalidArgumentException If $keys is neither an array nor a Traversable, + * or if any of the $keys are not a legal value. + */ + public function getMultiple($keys, $default = null) + { + $this->ensureValidKeys($keys); + + $results = $this->innerEngine->readMany($keys); + foreach ($results as $key => $value) { + if ($value === false) { + $results[$key] = $default; + } + } + + return $results; + } + + /** + * Persists a set of key => value pairs in the cache, with an optional TTL. + * + * @param iterable $values A list of key => value pairs for a multiple-set operation. + * @param null|int|\DateInterval $ttl Optional. The TTL value of this item. If no value is sent and + * the driver supports TTL then the library may set a default value + * for it or let the driver take care of that. + * @return bool True on success and false on failure. + * @throws \Cake\Cache\InvalidArgumentException If $values is neither an array nor a Traversable, + * or if any of the $values are not a legal value. + */ + public function setMultiple($values, $ttl = null) + { + $this->ensureValidKeys(array_keys($values)); + + if ($ttl !== null) { + $restore = $this->innerEngine->getConfig('duration'); + $this->innerEngine->setConfig('duration', $ttl); + } + try { + $result = $this->innerEngine->writeMany($values); + foreach ($result as $key => $success) { + if ($success === false) { + return false; + } + } + + return true; + } finally { + if (isset($restore)) { + $this->innerEngine->setConfig('duration', $restore); + } + } + + return false; + } + + /** + * Deletes multiple cache items in a single operation. + * + * @param iterable $keys A list of string-based keys to be deleted. + * @return bool True if the items were successfully removed. False if there was an error. + * @throws \Cake\Cache\InvalidArgumentException If $keys is neither an array nor a Traversable, + * or if any of the $keys are not a legal value. + */ + public function deleteMultiple($keys) + { + $this->ensureValidKeys($keys); + + $result = $this->innerEngine->deleteMany($keys); + foreach ($result as $key => $success) { + if ($success === false) { + return false; + } + } + + return true; + } + + /** + * Determines whether an item is present in the cache. + * + * NOTE: It is recommended that has() is only to be used for cache warming type purposes + * and not to be used within your live applications operations for get/set, as this method + * is subject to a race condition where your has() will return true and immediately after, + * another script can remove it making the state of your app out of date. + * + * @param string $key The cache item key. + * @return bool + * @throws \Cake\Cache\InvalidArgumentException If the $key string is not a legal value. + */ + public function has($key) + { + return $this->get($key) !== null; + } + + /** + * {@inheritDoc} + */ + public function add($key, $value) + { + return $this->innerEngine->add($key, $value); + } + + /** + * {@inheritDoc} + */ + public function increment($key, $offset = 1) + { + return $this->innerEngine->increment($key, $offset); + } + + /** + * {@inheritDoc} + */ + public function decrement($key, $offset = 1) + { + return $this->innerEngine->decrement($key, $offset); + } + + /** + * {@inheritDoc} + */ + public function clearGroup($group) + { + return $this->innerEngine->clearGroup($group); + } +} diff --git a/app/vendor/cakephp/cakephp/src/Cache/composer.json b/app/vendor/cakephp/cakephp/src/Cache/composer.json index 23289aa19..276030307 100644 --- a/app/vendor/cakephp/cakephp/src/Cache/composer.json +++ b/app/vendor/cakephp/cakephp/src/Cache/composer.json @@ -23,6 +23,7 @@ }, "require": { "php": ">=5.6.0", + "psr/simple-cache": "^1.0.0", "cakephp/core": "^3.6.0" }, "autoload": { diff --git a/app/vendor/cakephp/cakephp/src/Collection/CollectionInterface.php b/app/vendor/cakephp/cakephp/src/Collection/CollectionInterface.php index 951e23c92..1d4019246 100644 --- a/app/vendor/cakephp/cakephp/src/Collection/CollectionInterface.php +++ b/app/vendor/cakephp/cakephp/src/Collection/CollectionInterface.php @@ -21,6 +21,8 @@ * Describes the methods a Collection should implement. A collection is an immutable * list of elements exposing a number of traversing and extracting method for * generating other collections. + * + * @method \Cake\Collection\CollectionInterface cartesianProduct(callable $operation = null, callable $filter = null) */ interface CollectionInterface extends Iterator, JsonSerializable { @@ -557,6 +559,25 @@ public function sample($size = 10); */ public function take($size = 1, $from = 0); + /** + * Returns the last N elements of a collection + * + * ### Example: + * + * ``` + * $items = [1, 2, 3, 4, 5]; + * + * $last = (new Collection($items))->takeLast(3); + * + * // Result will look like this when converted to array + * [3, 4, 5]; + * ``` + * + * @param int $howMany The number of elements at the end of the collection + * @return \Cake\Collection\CollectionInterface + */ + public function takeLast($howMany); + /** * Returns a new collection that will skip the specified amount of elements * at the beginning of the iteration. @@ -783,6 +804,16 @@ public function jsonSerialize(); */ public function compile($preserveKeys = true); + /** + * Returns a new collection where any operations chained after it are guaranteed + * to be run lazily. That is, elements will be yieleded one at a time. + * + * A lazy collection can only be iterated once. A second attempt results in an error. + * + * @return \Cake\Collection\CollectionInterface + */ + public function lazy(); + /** * Returns a new collection where the operations performed by this collection. * No matter how many times the new collection is iterated, those operations will @@ -837,7 +868,7 @@ public function listNested($dir = 'desc', $nestingKey = 'children'); /** * Creates a new collection that when iterated will stop yielding results if - * the provided condition evaluates to false. + * the provided condition evaluates to true. * * This is handy for dealing with infinite iterators or any generator that * could start returning invalid elements at a certain point. For example, @@ -862,7 +893,7 @@ public function listNested($dir = 'desc', $nestingKey = 'children'); * ``` * * @param callable $condition the method that will receive each of the elements and - * returns false when the iteration should be stopped. + * returns true when the iteration should be stopped. * If an array, it will be interpreted as a key-value list of conditions where * the key is a property path as accepted by `Collection::extract`, * and the value the condition against with each element will be matched. diff --git a/app/vendor/cakephp/cakephp/src/Collection/CollectionTrait.php b/app/vendor/cakephp/cakephp/src/Collection/CollectionTrait.php index bb9fb6cf5..fac870082 100644 --- a/app/vendor/cakephp/cakephp/src/Collection/CollectionTrait.php +++ b/app/vendor/cakephp/cakephp/src/Collection/CollectionTrait.php @@ -35,13 +35,27 @@ use Traversable; /** - * Offers a handful of method to manipulate iterators + * Offers a handful of methods to manipulate iterators */ trait CollectionTrait { use ExtractTrait; + /** + * Returns a new collection. + * + * Allows classes which use this trait to determine their own + * type of returned collection interface + * + * @param mixed ...$args Constructor arguments. + * @return \Cake\Collection\CollectionInterface + */ + protected function newCollection(...$args) + { + return new Collection(...$args); + } + /** * {@inheritDoc} */ @@ -258,7 +272,7 @@ public function groupBy($callback) $group[$callback($value)][] = $value; } - return new Collection($group); + return $this->newCollection($group); } /** @@ -272,7 +286,7 @@ public function indexBy($callback) $group[$callback($value)] = $value; } - return new Collection($group); + return $this->newCollection($group); } /** @@ -283,14 +297,16 @@ public function countBy($callback) $callback = $this->_propertyExtractor($callback); $mapper = function ($value, $key, $mr) use ($callback) { + /** @var \Cake\Collection\Iterator\MapReduce $mr */ $mr->emitIntermediate($value, $callback($value)); }; $reducer = function ($values, $key, $mr) { + /** @var \Cake\Collection\Iterator\MapReduce $mr */ $mr->emit(count($values), $key); }; - return new Collection(new MapReduce($this->unwrap(), $mapper, $reducer)); + return $this->newCollection(new MapReduce($this->unwrap(), $mapper, $reducer)); } /** @@ -319,7 +335,7 @@ public function shuffle() $elements = $this->toArray(); shuffle($elements); - return new Collection($elements); + return $this->newCollection($elements); } /** @@ -327,7 +343,7 @@ public function shuffle() */ public function sample($size = 10) { - return new Collection(new LimitIterator($this->shuffle(), 0, $size)); + return $this->newCollection(new LimitIterator($this->shuffle(), 0, $size)); } /** @@ -335,7 +351,7 @@ public function sample($size = 10) */ public function take($size = 1, $from = 0) { - return new Collection(new LimitIterator($this, $from, $size)); + return $this->newCollection(new LimitIterator($this, $from, $size)); } /** @@ -343,7 +359,7 @@ public function take($size = 1, $from = 0) */ public function skip($howMany) { - return new Collection(new LimitIterator($this, $howMany)); + return $this->newCollection(new LimitIterator($this, $howMany)); } /** @@ -399,6 +415,105 @@ public function last() return $result; } + /** + * {@inheritDoc} + */ + public function takeLast($howMany) + { + if ($howMany < 1) { + throw new \InvalidArgumentException("The takeLast method requires a number greater than 0."); + } + + $iterator = $this->optimizeUnwrap(); + if (is_array($iterator)) { + return $this->newCollection(array_slice($iterator, $howMany * -1)); + } + + if ($iterator instanceof Countable) { + $count = count($iterator); + + if ($count === 0) { + return $this->newCollection([]); + } + + $iterator = new LimitIterator($iterator, max(0, $count - $howMany), $howMany); + + return $this->newCollection($iterator); + } + + $generator = function ($iterator, $howMany) { + $result = []; + $bucket = 0; + $offset = 0; + + /** + * Consider the collection of elements [1, 2, 3, 4, 5, 6, 7, 8, 9], in order + * to get the last 4 elements, we can keep a buffer of 4 elements and + * fill it circularly using modulo logic, we use the $bucket variable + * to track the position to fill next in the buffer. This how the buffer + * looks like after 4 iterations: + * + * 0) 1 2 3 4 -- $bucket now goes back to 0, we have filled 4 elementes + * 1) 5 2 3 4 -- 5th iteration + * 2) 5 6 3 4 -- 6th iteration + * 3) 5 6 7 4 -- 7th iteration + * 4) 5 6 7 8 -- 8th iteration + * 5) 9 6 7 8 + * + * We can see that at the end of the iterations, the buffer contains all + * the last four elements, just in the wrong order. How do we keep the + * original order? Well, it turns out that the number of iteration also + * give us a clue on what's going on, Let's add a marker for it now: + * + * 0) 1 2 3 4 + * ^ -- The 0) above now becomes the $offset variable + * 1) 5 2 3 4 + * ^ -- $offset = 1 + * 2) 5 6 3 4 + * ^ -- $offset = 2 + * 3) 5 6 7 4 + * ^ -- $offset = 3 + * 4) 5 6 7 8 + * ^ -- We use module logic for $offset too + * and as you can see each time $offset is 0, then the buffer + * is sorted exactly as we need. + * 5) 9 6 7 8 + * ^ -- $offset = 1 + * + * The $offset variable is a marker for splitting the buffer in two, + * elements to the right for the marker are the head of the final result, + * whereas the elements at the left are the tail. For example consider step 5) + * which has an offset of 1: + * + * - $head = elements to the right = [6, 7, 8] + * - $tail = elements to the left = [9] + * - $result = $head + $tail = [6, 7, 8, 9] + * + * The logic above applies to collections of any size. + */ + + foreach ($iterator as $k => $item) { + $result[$bucket] = [$k, $item]; + $bucket = (++$bucket) % $howMany; + $offset++; + } + + $offset = $offset % $howMany; + $head = array_slice($result, $offset); + $tail = array_slice($result, 0, $offset); + + foreach ($head as $v) { + yield $v[0] => $v[1]; + } + + foreach ($tail as $v) { + yield $v[0] => $v[1]; + } + }; + + return $this->newCollection($generator($iterator, $howMany)); + } + /** * {@inheritDoc} */ @@ -406,9 +521,9 @@ public function append($items) { $list = new AppendIterator(); $list->append($this->unwrap()); - $list->append((new Collection($items))->unwrap()); + $list->append($this->newCollection($items)->unwrap()); - return new Collection($list); + return $this->newCollection($list); } /** @@ -430,7 +545,7 @@ public function appendItem($item, $key = null) */ public function prepend($items) { - return (new Collection($items))->append($this); + return $this->newCollection($items)->append($this); } /** @@ -459,6 +574,7 @@ public function combine($keyPath, $valuePath, $groupPath = null) ]; $mapper = function ($value, $key, $mapReduce) use ($options) { + /** @var \Cake\Collection\Iterator\MapReduce $mapReduce */ $rowKey = $options['keyPath']; $rowVal = $options['valuePath']; @@ -480,10 +596,11 @@ public function combine($keyPath, $valuePath, $groupPath = null) foreach ($values as $value) { $result += $value; } + /** @var \Cake\Collection\Iterator\MapReduce $mapReduce */ $mapReduce->emit($result, $key); }; - return new Collection(new MapReduce($this->unwrap(), $mapper, $reducer)); + return $this->newCollection(new MapReduce($this->unwrap(), $mapper, $reducer)); } /** @@ -501,6 +618,7 @@ public function nest($idPath, $parentPath, $nestingKey = 'children') $id = $idPath($row, $key); $parentId = $parentPath($row, $key); $parents[$id] =& $row; + /** @var \Cake\Collection\Iterator\MapReduce $mapReduce */ $mapReduce->emitIntermediate($id, $parentId); }; @@ -513,6 +631,7 @@ public function nest($idPath, $parentPath, $nestingKey = 'children') if (empty($key) || !isset($parents[$key])) { foreach ($values as $id) { $parents[$id] = $isObject ? $parents[$id] : new ArrayIterator($parents[$id], 1); + /** @var \Cake\Collection\Iterator\MapReduce $mapReduce */ $mapReduce->emit($parents[$id]); } @@ -526,8 +645,9 @@ public function nest($idPath, $parentPath, $nestingKey = 'children') $parents[$key][$nestingKey] = $children; }; - return (new Collection(new MapReduce($this->unwrap(), $mapper, $reducer))) + return $this->newCollection(new MapReduce($this->unwrap(), $mapper, $reducer)) ->map(function ($value) use (&$isObject) { + /** @var \ArrayIterator $value */ return $isObject ? $value : $value->getArrayCopy(); }); } @@ -583,7 +703,21 @@ public function jsonSerialize() */ public function compile($preserveKeys = true) { - return new Collection($this->toArray($preserveKeys)); + return $this->newCollection($this->toArray($preserveKeys)); + } + + /** + * {@inheritDoc} + */ + public function lazy() + { + $generator = function () { + foreach ($this->unwrap() as $k => $v) { + yield $k => $v; + } + }; + + return $this->newCollection($generator()); } /** @@ -641,7 +775,7 @@ public function unfold(callable $transformer = null) }; } - return new Collection( + return $this->newCollection( new RecursiveIteratorIterator( new UnfoldIterator($this->unwrap(), $transformer), RecursiveIteratorIterator::LEAVES_ONLY @@ -656,7 +790,7 @@ public function through(callable $handler) { $result = $handler($this); - return $result instanceof CollectionInterface ? $result : new Collection($result); + return $result instanceof CollectionInterface ? $result : $this->newCollection($result); } /** @@ -772,14 +906,15 @@ public function _unwrap() } /** - * {@inheritDoc} - * + * @param callable|null $operation Operation + * @param callable|null $filter Filter * @return \Cake\Collection\CollectionInterface + * @throws \LogicException */ public function cartesianProduct(callable $operation = null, callable $filter = null) { if ($this->isEmpty()) { - return new Collection([]); + return $this->newCollection([]); } $collectionArrays = []; @@ -821,13 +956,14 @@ public function cartesianProduct(callable $operation = null, callable $filter = } } - return new Collection($result); + return $this->newCollection($result); } /** * {@inheritDoc} * * @return \Cake\Collection\CollectionInterface + * @throws \LogicException */ public function transpose() { @@ -844,7 +980,7 @@ public function transpose() $result[] = array_column($arrayValue, $column); } - return new Collection($result); + return $this->newCollection($result); } /** diff --git a/app/vendor/cakephp/cakephp/src/Collection/ExtractTrait.php b/app/vendor/cakephp/cakephp/src/Collection/ExtractTrait.php index 2beb22dd3..840135354 100644 --- a/app/vendor/cakephp/cakephp/src/Collection/ExtractTrait.php +++ b/app/vendor/cakephp/cakephp/src/Collection/ExtractTrait.php @@ -57,7 +57,7 @@ protected function _propertyExtractor($callback) * It will return arrays for elements in represented with `{*}` * * @param array|\ArrayAccess $data Data. - * @param array $path Path to extract from. + * @param string[] $path Path to extract from. * @return mixed */ protected function _extract($data, $path) @@ -98,7 +98,7 @@ protected function _extract($data, $path) * by iterating over the column names contained in $path * * @param array|\ArrayAccess $data Data. - * @param array $path Path to extract from. + * @param string[] $path Path to extract from. * @return mixed */ protected function _simpleExtract($data, $path) diff --git a/app/vendor/cakephp/cakephp/src/Collection/Iterator/FilterIterator.php b/app/vendor/cakephp/cakephp/src/Collection/Iterator/FilterIterator.php index c5cbdf3aa..2ff534ca4 100644 --- a/app/vendor/cakephp/cakephp/src/Collection/Iterator/FilterIterator.php +++ b/app/vendor/cakephp/cakephp/src/Collection/Iterator/FilterIterator.php @@ -43,7 +43,7 @@ class FilterIterator extends Collection * in the current iteration, the key of the element and the passed $items iterator * as arguments, in that order. * - * @param \Iterator $items The items to be filtered. + * @param \Traversable|array $items The items to be filtered. * @param callable $callback Callback. */ public function __construct($items, callable $callback) diff --git a/app/vendor/cakephp/cakephp/src/Command/HelpCommand.php b/app/vendor/cakephp/cakephp/src/Command/HelpCommand.php index b722da208..d4d7fbbf9 100644 --- a/app/vendor/cakephp/cakephp/src/Command/HelpCommand.php +++ b/app/vendor/cakephp/cakephp/src/Command/HelpCommand.php @@ -21,6 +21,7 @@ use Cake\Console\ConsoleIo; use Cake\Console\ConsoleOptionParser; use Cake\Console\ConsoleOutput; +use Cake\Utility\Inflector; use SimpleXMLElement; /** @@ -44,7 +45,7 @@ public function setCommandCollection(CommandCollection $commands) } /** - * Main function Prints out the list of shells. + * Main function Prints out the list of commands. * * @param \Cake\Console\Arguments $args The command arguments. * @param \Cake\Console\ConsoleIo $io The console io @@ -54,9 +55,9 @@ public function execute(Arguments $args, ConsoleIo $io) { if (!$args->getOption('xml')) { $io->out('Current Paths:', 2); - $io->out('* app: ' . APP_DIR); - $io->out('* root: ' . rtrim(ROOT, DIRECTORY_SEPARATOR)); - $io->out('* core: ' . rtrim(CORE_PATH, DIRECTORY_SEPARATOR)); + $io->out('* app: ' . APP_DIR . DIRECTORY_SEPARATOR); + $io->out('* root: ' . ROOT . DIRECTORY_SEPARATOR); + $io->out('* core: ' . CORE_PATH); $io->out(''); $io->out('Available Commands:', 2); @@ -70,6 +71,7 @@ public function execute(Arguments $args, ConsoleIo $io) return static::CODE_SUCCESS; } + $this->asText($io, $commands); return static::CODE_SUCCESS; @@ -95,29 +97,72 @@ protected function asText($io, $commands) $invert[$class][] = $name; } - foreach ($commands as $name => $class) { - if (is_object($class)) { - $class = get_class($class); + $prefixed = []; + foreach ($invert as $class => $names) { + $prefixedName = $this->findPrefixedName($names); + if (!$prefixedName) { + $prefix = preg_match('#^Cake\\\\(Command|Shell)\\\\#', $class) ? '[core]' : '[app]'; + $prefixed[$prefix][] = $this->getShortestName($names); + continue; } - if (count($invert[$class]) == 1) { - $io->out('- ' . $name); + + list ($prefix, $name) = explode('.', $prefixedName, 2); + $prefix = Inflector::camelize($prefix); + + $shortestName = $this->getShortestName($names); + if (strpos($shortestName, '.') !== false) { + list (, $shortestName) = explode('.', $shortestName, 2); } - if (count($invert[$class]) > 1) { - // Sort by length so we can get the shortest name. - usort($invert[$class], function ($a, $b) { - return strlen($a) - strlen($b); - }); - $io->out('- ' . array_shift($invert[$class])); + $prefixed[$prefix][] = $shortestName; + } - // Empty the list to prevent duplicates - $invert[$class] = []; + ksort($prefixed); + + foreach ($prefixed as $prefix => $names) { + $io->out($prefix . ':'); + sort($names); + foreach ($names as $name) { + $io->out(' - ' . $name); } } $io->out(''); - $io->out('To run a command, type `cake shell_name [args|options]`'); - $io->out('To get help on a specific command, type `cake shell_name --help`', 2); + $io->out('To run a command, type `cake command_name [args|options]`'); + $io->out('To get help on a specific command, type `cake command_name --help`', 2); + } + + /** + * @param string[] $names Names + * + * @return string|null + */ + protected function findPrefixedName(array $names) + { + foreach ($names as $name) { + if (strpos($name, '.') !== false) { + return $name; + } + } + + return null; + } + + /** + * @param string[] $names Names + * @return string + */ + protected function getShortestName(array $names) + { + if (count($names) <= 1) { + return array_shift($names); + } + + usort($names, function ($a, $b) { + return strlen($a) - strlen($b); + }); + + return array_shift($names); } /** @@ -153,7 +198,7 @@ protected function asXml($io, $commands) protected function buildOptionParser(ConsoleOptionParser $parser) { $parser->setDescription( - 'Get the list of available shells for this application.' + 'Get the list of available commands for this application.' )->addOption('xml', [ 'help' => 'Get the listing as XML.', 'boolean' => true diff --git a/app/vendor/cakephp/cakephp/src/Console/Arguments.php b/app/vendor/cakephp/cakephp/src/Console/Arguments.php index 6d7035d99..718ee7286 100644 --- a/app/vendor/cakephp/cakephp/src/Console/Arguments.php +++ b/app/vendor/cakephp/cakephp/src/Console/Arguments.php @@ -117,7 +117,7 @@ public function hasArgument($name) public function getArgument($name) { $offset = array_search($name, $this->argNames, true); - if ($offset === false) { + if ($offset === false || !isset($this->args[$offset])) { return null; } diff --git a/app/vendor/cakephp/cakephp/src/Console/Command.php b/app/vendor/cakephp/cakephp/src/Console/Command.php index 91fbde1f5..0cd80bc9f 100644 --- a/app/vendor/cakephp/cakephp/src/Console/Command.php +++ b/app/vendor/cakephp/cakephp/src/Console/Command.php @@ -63,6 +63,10 @@ public function __construct() $this->modelFactory('Table', function ($alias) { return $this->getTableLocator()->get($alias); }); + + if (isset($this->modelClass)) { + $this->loadModel(); + } } /** @@ -245,4 +249,35 @@ public function abort($code = self::CODE_ERROR) { throw new StopException('Command aborted', $code); } + + /** + * Execute another command with the provided set of arguments. + * + * @param string|\Cake\Console\Command $command The command class name or command instance. + * @param array $args The arguments to invoke the command with. + * @param \Cake\Console\ConsoleIo $io The ConsoleIo instance to use for the executed command. + * @return int|null The exit code or null for success of the command. + */ + public function executeCommand($command, array $args = [], ConsoleIo $io = null) + { + if (is_string($command)) { + if (!class_exists($command)) { + throw new InvalidArgumentException("Command class '{$command}' does not exist."); + } + $command = new $command(); + } + if (!$command instanceof Command) { + $commandType = getTypeName($command); + throw new InvalidArgumentException( + "Command '{$commandType}' is not a subclass of Cake\Console\Command." + ); + } + $io = $io ?: new ConsoleIo(); + + try { + return $command->run($args, $io); + } catch (StopException $e) { + return $e->getCode(); + } + } } diff --git a/app/vendor/cakephp/cakephp/src/Console/CommandCollection.php b/app/vendor/cakephp/cakephp/src/Console/CommandCollection.php index 294ca73fd..c7e45def4 100644 --- a/app/vendor/cakephp/cakephp/src/Console/CommandCollection.php +++ b/app/vendor/cakephp/cakephp/src/Console/CommandCollection.php @@ -53,6 +53,7 @@ public function __construct(array $commands = []) * @param string $name The name of the command you want to map. * @param string|\Cake\Console\Shell|\Cake\Console\Command $command The command to map. * @return $this + * @throws \InvalidArgumentException */ public function add($name, $command) { @@ -64,6 +65,11 @@ public function add($name, $command) "Cannot use '$class' for command '$name' it is not a subclass of Cake\Console\Shell or Cake\Console\Command." ); } + if (!preg_match('/^[^\s]+(?:(?: [^\s]+){1,2})?$/ui', $name)) { + throw new InvalidArgumentException( + "The command name `{$name}` is invalid. Names can only be a maximum of three words." + ); + } $this->commands[$name] = $command; @@ -157,7 +163,7 @@ public function count() * the long name (`plugin.command`) will be returned. * * @param string $plugin The plugin to scan. - * @return array Discovered plugin commands. + * @return string[] Discovered plugin commands. */ public function discoverPlugin($plugin) { @@ -171,7 +177,7 @@ public function discoverPlugin($plugin) * Resolve names based on existing commands * * @param array $input The results of a CommandScanner operation. - * @return array A flat map of command names => class names. + * @return string[] A flat map of command names => class names. */ protected function resolveNames(array $input) { @@ -208,7 +214,7 @@ protected function resolveNames(array $input) * Commands defined in the application will ovewrite commands with * the same name provided by CakePHP. * - * @return array An array of command names and their classes. + * @return string[] An array of command names and their classes. */ public function autoDiscover() { diff --git a/app/vendor/cakephp/cakephp/src/Console/CommandRunner.php b/app/vendor/cakephp/cakephp/src/Console/CommandRunner.php index 9638d7f18..e0ee1022e 100644 --- a/app/vendor/cakephp/cakephp/src/Console/CommandRunner.php +++ b/app/vendor/cakephp/cakephp/src/Console/CommandRunner.php @@ -16,11 +16,7 @@ use Cake\Command\HelpCommand; use Cake\Command\VersionCommand; -use Cake\Console\CommandCollection; -use Cake\Console\CommandCollectionAwareInterface; -use Cake\Console\ConsoleIo; use Cake\Console\Exception\StopException; -use Cake\Console\Shell; use Cake\Core\ConsoleApplicationInterface; use Cake\Core\HttpApplicationInterface; use Cake\Core\PluginApplicationInterface; @@ -106,7 +102,7 @@ public function __construct(ConsoleApplicationInterface $app, $root = 'cake', Co * $runner->setAliases(['--version' => 'version']); * ``` * - * @param array $aliases The map of aliases to replace. + * @param string[] $aliases The map of aliases to replace. * @return $this */ public function setAliases(array $aliases) @@ -156,25 +152,27 @@ public function run(array $argv, ConsoleIo $io = null) array_shift($argv); $io = $io ?: new ConsoleIo(); - $name = $this->resolveName($commands, $io, array_shift($argv)); - $result = Shell::CODE_ERROR; + list($name, $argv) = $this->longestCommandName($commands, $argv); + $name = $this->resolveName($commands, $io, $name); + + $result = Command::CODE_ERROR; $shell = $this->getShell($io, $commands, $name); if ($shell instanceof Shell) { $result = $this->runShell($shell, $argv); } if ($shell instanceof Command) { - $result = $shell->run($argv, $io); + $result = $this->runCommand($shell, $argv, $io); } if ($result === null || $result === true) { - return Shell::CODE_SUCCESS; + return Command::CODE_SUCCESS; } - if (is_int($result)) { + if (is_int($result) && $result >= 0 && $result <= 255) { return $result; } - return Shell::CODE_ERROR; + return Command::CODE_ERROR; } /** @@ -295,15 +293,43 @@ protected function getShell(ConsoleIo $io, CommandCollection $commands, $name) return $instance; } + /** + * Build the longest command name that exists in the collection + * + * Build the longest command name that matches a + * defined command. This will traverse a maximum of 3 tokens. + * + * @param \Cake\Console\CommandCollection $commands The command collection to check. + * @param array $argv The CLI arguments. + * @return array An array of the resolved name and modified argv. + */ + protected function longestCommandName($commands, $argv) + { + for ($i = 3; $i > 1; $i--) { + $parts = array_slice($argv, 0, $i); + $name = implode(' ', $parts); + if ($commands->has($name)) { + return [$name, array_slice($argv, $i)]; + } + } + $name = array_shift($argv); + + return [$name, $argv]; + } + /** * Resolve the command name into a name that exists in the collection. * * Apply backwards compatible inflections and aliases. + * Will step forward up to 3 tokens in $argv to generate + * a command name in the CommandCollection. More specific + * command names take precedence over less specific ones. * * @param \Cake\Console\CommandCollection $commands The command collection to check. * @param \Cake\Console\ConsoleIo $io ConsoleIo object for errors. - * @param string $name The name from the CLI args. - * @return string The resolved name. + * @param string $name The name + * @return string The resolved class name + * @throws \RuntimeException */ protected function resolveName($commands, $io, $name) { @@ -327,6 +353,23 @@ protected function resolveName($commands, $io, $name) return $name; } + /** + * Execute a Command class. + * + * @param \Cake\Console\Command $command The command to run. + * @param array $argv The CLI arguments to invoke. + * @param \Cake\Console\ConsoleIo $io The console io + * @return int Exit code + */ + protected function runCommand(Command $command, array $argv, ConsoleIo $io) + { + try { + return $command->run($argv, $io); + } catch (StopException $e) { + return $e->getCode(); + } + } + /** * Execute a Shell class. * diff --git a/app/vendor/cakephp/cakephp/src/Console/CommandScanner.php b/app/vendor/cakephp/cakephp/src/Console/CommandScanner.php index b8f86db05..a19cd8606 100644 --- a/app/vendor/cakephp/cakephp/src/Console/CommandScanner.php +++ b/app/vendor/cakephp/cakephp/src/Console/CommandScanner.php @@ -83,7 +83,7 @@ public function scanApp() */ public function scanPlugin($plugin) { - if (!Plugin::loaded($plugin)) { + if (!Plugin::isLoaded($plugin)) { return []; } $path = Plugin::classPath($plugin); @@ -103,7 +103,7 @@ public function scanPlugin($plugin) * @param string $path The directory to read. * @param string $namespace The namespace the shells live in. * @param string $prefix The prefix to apply to commands for their full name. - * @param array $hide A list of command names to hide as they are internal commands. + * @param string[] $hide A list of command names to hide as they are internal commands. * @return array The list of shell info arrays based on scanning the filesystem and inflection. */ protected function scanDir($path, $namespace, $prefix, array $hide) diff --git a/app/vendor/cakephp/cakephp/src/Console/ConsoleInput.php b/app/vendor/cakephp/cakephp/src/Console/ConsoleInput.php index 6a45b5865..7f3b2bbab 100644 --- a/app/vendor/cakephp/cakephp/src/Console/ConsoleInput.php +++ b/app/vendor/cakephp/cakephp/src/Console/ConsoleInput.php @@ -77,7 +77,9 @@ public function read() public function dataAvailable($timeout = 0) { $readFds = [$this->_input]; - $readyFds = stream_select($readFds, null, null, $timeout); + $writeFds = null; + $errorFds = null; + $readyFds = stream_select($readFds, $writeFds, $errorFds, $timeout); return ($readyFds > 0); } diff --git a/app/vendor/cakephp/cakephp/src/Console/ConsoleInputOption.php b/app/vendor/cakephp/cakephp/src/Console/ConsoleInputOption.php index 87d280a97..0a6af94f4 100644 --- a/app/vendor/cakephp/cakephp/src/Console/ConsoleInputOption.php +++ b/app/vendor/cakephp/cakephp/src/Console/ConsoleInputOption.php @@ -83,7 +83,7 @@ class ConsoleInputOption * @param string $help The help text for this option * @param bool $boolean Whether this option is a boolean option. Boolean options don't consume extra tokens * @param string $default The default value for this option. - * @param array $choices Valid choices for this option. + * @param string[] $choices Valid choices for this option. * @param bool $multiple Whether this option can accept multiple value definition. * @throws \Cake\Console\Exception\ConsoleException */ diff --git a/app/vendor/cakephp/cakephp/src/Console/ConsoleIo.php b/app/vendor/cakephp/cakephp/src/Console/ConsoleIo.php index 49042dd20..77ecf33eb 100644 --- a/app/vendor/cakephp/cakephp/src/Console/ConsoleIo.php +++ b/app/vendor/cakephp/cakephp/src/Console/ConsoleIo.php @@ -254,8 +254,8 @@ public function success($message = null, $newlines = 1, $level = self::NORMAL) * Wraps a message with a given message type, e.g. * * @param string $messageType The message type, e.g. "warning". - * @param string|array $message The message to wrap. - * @return array|string The message wrapped with the given message type. + * @param string|string[] $message The message to wrap. + * @return string|string[] The message wrapped with the given message type. */ protected function wrapMessageWithType($messageType, $message) { @@ -353,7 +353,7 @@ public function hr($newlines = 0, $width = 79) * * @param string $prompt Prompt text. * @param string|null $default Default input value. - * @return mixed Either the default value, or the user-provided input. + * @return string Either the default value, or the user-provided input. */ public function ask($prompt, $default = null) { @@ -390,15 +390,15 @@ public function outputAs($mode) * Add a new output style or get defined styles. * * @param string|null $style The style to get or create. - * @param array|bool|null $definition The array definition of the style to change or create a style + * @param array|false|null $definition The array definition of the style to change or create a style * or false to remove a style. - * @return mixed If you are getting styles, the style or null will be returned. If you are creating/modifying + * @return array|null|true If you are getting styles, the style or null will be returned. If you are creating/modifying * styles true will be returned. * @see \Cake\Console\ConsoleOutput::styles() */ public function styles($style = null, $definition = null) { - $this->_out->styles($style, $definition); + return $this->_out->styles($style, $definition); } /** @@ -407,7 +407,7 @@ public function styles($style = null, $definition = null) * @param string $prompt Prompt text. * @param string|array $options Array or string of options. * @param string|null $default Default input value. - * @return mixed Either the default value, or the user-provided input. + * @return string Either the default value, or the user-provided input. */ public function askChoice($prompt, $options, $default = null) { @@ -540,12 +540,6 @@ public function helper($name, array $settings = []) */ public function createFile($path, $contents, $forceOverwrite = false) { - $path = str_replace( - DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR, - DIRECTORY_SEPARATOR, - $path - ); - $this->out(); $forceOverwrite = $forceOverwrite || $this->forceOverwrite; @@ -572,6 +566,12 @@ public function createFile($path, $contents, $forceOverwrite = false) } try { + // Create the directory using the current user permissions. + $directory = dirname($path); + if (!file_exists($directory)) { + mkdir($directory, 0777 ^ umask(), true); + } + $file = new SplFileObject($path, 'w'); } catch (RuntimeException $e) { $this->error("Could not write to `{$path}`. Permission denied.", 2); diff --git a/app/vendor/cakephp/cakephp/src/Console/ConsoleOptionParser.php b/app/vendor/cakephp/cakephp/src/Console/ConsoleOptionParser.php index 52f479529..b9bd90799 100644 --- a/app/vendor/cakephp/cakephp/src/Console/ConsoleOptionParser.php +++ b/app/vendor/cakephp/cakephp/src/Console/ConsoleOptionParser.php @@ -949,7 +949,7 @@ protected function getShortOptionError($option) * algorithm. * * @param string $needle Unknown item (either a subcommand name or an option for instance) trying to be used. - * @param array $haystack List of items available for the type $needle belongs to. + * @param string[] $haystack List of items available for the type $needle belongs to. * @return string|null The closest name to the item submitted by the user. */ protected function findClosestItem($needle, $haystack) @@ -1073,8 +1073,8 @@ protected function _optionExists($name) if (substr($name, 0, 2) === '--') { return isset($this->_options[substr($name, 2)]); } - if ($name{0} === '-' && $name{1} !== '-') { - return isset($this->_shortOptions[$name{1}]); + if ($name[0] === '-' && $name[1] !== '-') { + return isset($this->_shortOptions[$name[1]]); } return false; @@ -1086,7 +1086,7 @@ protected function _optionExists($name) * * @param string $argument The argument to append * @param array $args The array of parsed args to append to. - * @return array Args + * @return string[] Args * @throws \Cake\Console\Exception\ConsoleException */ protected function _parseArg($argument, $args) diff --git a/app/vendor/cakephp/cakephp/src/Console/ConsoleOutput.php b/app/vendor/cakephp/cakephp/src/Console/ConsoleOutput.php index d16ba8048..f2d09cd34 100644 --- a/app/vendor/cakephp/cakephp/src/Console/ConsoleOutput.php +++ b/app/vendor/cakephp/cakephp/src/Console/ConsoleOutput.php @@ -220,6 +220,7 @@ public function styleText($text) */ protected function _replaceTags($matches) { + /** @var array $style */ $style = $this->styles($matches['tag']); if (empty($style)) { return '<' . $matches['tag'] . '>' . $matches['text'] . ''; @@ -239,7 +240,7 @@ protected function _replaceTags($matches) } } - return "\033[" . implode($styleInfo, ';') . 'm' . $matches['text'] . "\033[0m"; + return "\033[" . implode(';', $styleInfo) . 'm' . $matches['text'] . "\033[0m"; } /** @@ -281,9 +282,9 @@ protected function _write($message) * ``` * * @param string|null $style The style to get or create. - * @param array|bool|null $definition The array definition of the style to change or create a style + * @param array|false|null $definition The array definition of the style to change or create a style * or false to remove a style. - * @return mixed If you are getting styles, the style or null will be returned. If you are creating/modifying + * @return array|true|null If you are getting styles, the style or null will be returned. If you are creating/modifying * styles true will be returned. */ public function styles($style = null, $definition = null) diff --git a/app/vendor/cakephp/cakephp/src/Console/Exception/ConsoleException.php b/app/vendor/cakephp/cakephp/src/Console/Exception/ConsoleException.php index 445af8d90..4af7906bb 100644 --- a/app/vendor/cakephp/cakephp/src/Console/Exception/ConsoleException.php +++ b/app/vendor/cakephp/cakephp/src/Console/Exception/ConsoleException.php @@ -13,6 +13,7 @@ */ namespace Cake\Console\Exception; +use Cake\Console\Command; use Cake\Core\Exception\Exception; /** @@ -21,4 +22,10 @@ */ class ConsoleException extends Exception { + /** + * Default exception code + * + * @var int + */ + protected $_defaultCode = Command::CODE_ERROR; } diff --git a/app/vendor/cakephp/cakephp/src/Console/Exception/MissingHelperException.php b/app/vendor/cakephp/cakephp/src/Console/Exception/MissingHelperException.php index f5deef5b9..e2530a8c5 100644 --- a/app/vendor/cakephp/cakephp/src/Console/Exception/MissingHelperException.php +++ b/app/vendor/cakephp/cakephp/src/Console/Exception/MissingHelperException.php @@ -12,13 +12,10 @@ */ namespace Cake\Console\Exception; -use Cake\Core\Exception\Exception; - /** * Used when a Helper cannot be found. */ -class MissingHelperException extends Exception +class MissingHelperException extends ConsoleException { - protected $_messageTemplate = 'Helper class %s could not be found.'; } diff --git a/app/vendor/cakephp/cakephp/src/Console/Exception/MissingShellException.php b/app/vendor/cakephp/cakephp/src/Console/Exception/MissingShellException.php index 142fd92c9..be628ec81 100644 --- a/app/vendor/cakephp/cakephp/src/Console/Exception/MissingShellException.php +++ b/app/vendor/cakephp/cakephp/src/Console/Exception/MissingShellException.php @@ -12,12 +12,10 @@ */ namespace Cake\Console\Exception; -use Cake\Core\Exception\Exception; - /** * Used when a shell cannot be found. */ -class MissingShellException extends Exception +class MissingShellException extends ConsoleException { protected $_messageTemplate = 'Shell class for "%s" could not be found. If you are trying to use a plugin shell, that was loaded via $this->addPlugin(), you may need to update bin/cake.php to match https://github.com/cakephp/app/tree/master/bin/cake.php'; diff --git a/app/vendor/cakephp/cakephp/src/Console/Exception/MissingShellMethodException.php b/app/vendor/cakephp/cakephp/src/Console/Exception/MissingShellMethodException.php index 7c9251f3f..50ebeb322 100644 --- a/app/vendor/cakephp/cakephp/src/Console/Exception/MissingShellMethodException.php +++ b/app/vendor/cakephp/cakephp/src/Console/Exception/MissingShellMethodException.php @@ -12,12 +12,10 @@ */ namespace Cake\Console\Exception; -use Cake\Core\Exception\Exception; - /** * Used when a shell method cannot be found. */ -class MissingShellMethodException extends Exception +class MissingShellMethodException extends ConsoleException { protected $_messageTemplate = "Unknown command %1\$s %2\$s.\nFor usage try `cake %1\$s --help`"; diff --git a/app/vendor/cakephp/cakephp/src/Console/Exception/MissingTaskException.php b/app/vendor/cakephp/cakephp/src/Console/Exception/MissingTaskException.php index bde71c551..8dc49b7f7 100644 --- a/app/vendor/cakephp/cakephp/src/Console/Exception/MissingTaskException.php +++ b/app/vendor/cakephp/cakephp/src/Console/Exception/MissingTaskException.php @@ -12,13 +12,10 @@ */ namespace Cake\Console\Exception; -use Cake\Core\Exception\Exception; - /** * Used when a Task cannot be found. */ -class MissingTaskException extends Exception +class MissingTaskException extends ConsoleException { - protected $_messageTemplate = 'Task class %s could not be found.'; } diff --git a/app/vendor/cakephp/cakephp/src/Console/Exception/StopException.php b/app/vendor/cakephp/cakephp/src/Console/Exception/StopException.php index 955f83cb6..884ccf0cc 100644 --- a/app/vendor/cakephp/cakephp/src/Console/Exception/StopException.php +++ b/app/vendor/cakephp/cakephp/src/Console/Exception/StopException.php @@ -13,14 +13,13 @@ */ namespace Cake\Console\Exception; -use Cake\Core\Exception\Exception; - /** * Exception class for halting errors in console tasks * * @see \Cake\Console\Shell::_stop() * @see \Cake\Console\Shell::error() + * @see \Cake\Console\Command::abort() */ -class StopException extends Exception +class StopException extends ConsoleException { } diff --git a/app/vendor/cakephp/cakephp/src/Console/Shell.php b/app/vendor/cakephp/cakephp/src/Console/Shell.php index b07b9ca43..df8024bde 100644 --- a/app/vendor/cakephp/cakephp/src/Console/Shell.php +++ b/app/vendor/cakephp/cakephp/src/Console/Shell.php @@ -639,7 +639,7 @@ public function param($name) * @param string $prompt Prompt text. * @param string|array|null $options Array or string of options. * @param string|null $default Default input value. - * @return mixed Either the default value, or the user-provided input. + * @return string|null Either the default value, or the user-provided input. * @link https://book.cakephp.org/3.0/en/console-and-shells.html#Shell::in */ public function in($prompt, $options = null, $default = null) @@ -875,12 +875,14 @@ public function error($title, $message = null, $exitCode = self::CODE_ERROR) */ public function clear() { - if (empty($this->params['noclear'])) { - if (DIRECTORY_SEPARATOR === '/') { - passthru('clear'); - } else { - passthru('cls'); - } + if (!empty($this->params['noclear'])) { + return; + } + + if (DIRECTORY_SEPARATOR === '/') { + passthru('clear'); + } else { + passthru('cls'); } } diff --git a/app/vendor/cakephp/cakephp/src/Controller/Component.php b/app/vendor/cakephp/cakephp/src/Controller/Component.php index 408d18c2e..726e1b64a 100644 --- a/app/vendor/cakephp/cakephp/src/Controller/Component.php +++ b/app/vendor/cakephp/cakephp/src/Controller/Component.php @@ -55,11 +55,9 @@ * * @link https://book.cakephp.org/3.0/en/controllers/components.html * @see \Cake\Controller\Controller::$components - * @mixin \Cake\Core\InstanceConfigTrait */ class Component implements EventListenerInterface { - use InstanceConfigTrait; use LogTrait; @@ -161,7 +159,7 @@ public function initialize(array $config) * Magic method for lazy loading $components. * * @param string $name Name of component to get. - * @return mixed A Component object or null. + * @return \Cake\Controller\Component|null A Component object or null. */ public function __get($name) { diff --git a/app/vendor/cakephp/cakephp/src/Controller/Component/AuthComponent.php b/app/vendor/cakephp/cakephp/src/Controller/Component/AuthComponent.php index 2cd5061ed..9420bdd38 100644 --- a/app/vendor/cakephp/cakephp/src/Controller/Component/AuthComponent.php +++ b/app/vendor/cakephp/cakephp/src/Controller/Component/AuthComponent.php @@ -199,7 +199,7 @@ class AuthComponent extends Component /** * Controller actions for which user validation is not required. * - * @var array + * @var string[] * @see \Cake\Controller\Component\AuthComponent::allow() */ public $allowedActions = []; @@ -231,7 +231,7 @@ class AuthComponent extends Component * successfully logging in the current user after calling `login()` * in the same request * - * @var \Cake\Auth\BaseAuthenticate + * @var \Cake\Auth\BaseAuthenticate|null */ protected $_authenticationProvider; @@ -239,7 +239,7 @@ class AuthComponent extends Component * The instance of the Authorize provider that was used to grant * access to the current user to the URL they are requesting. * - * @var \Cake\Auth\BaseAuthorize + * @var \Cake\Auth\BaseAuthorize|null */ protected $_authorizationProvider; @@ -616,7 +616,7 @@ public function getAuthorize($alias) * $this->Auth->allow(); * ``` * - * @param string|array|null $actions Controller action name or array of actions + * @param string|string[]|null $actions Controller action name or array of actions * @return void * @link https://book.cakephp.org/3.0/en/controllers/components/authentication.html#making-actions-public */ @@ -646,7 +646,7 @@ public function allow($actions = null) * ``` * to remove all items from the allowed list * - * @param string|array|null $actions Controller action name or array of actions + * @param string|string[]|null $actions Controller action name or array of actions * @return void * @see \Cake\Controller\Component\AuthComponent::allow() * @link https://book.cakephp.org/3.0/en/controllers/components/authentication.html#making-actions-require-authorization @@ -659,7 +659,7 @@ public function deny($actions = null) return; } foreach ((array)$actions as $action) { - $i = array_search($action, $this->allowedActions); + $i = array_search($action, $this->allowedActions, true); if (is_int($i)) { unset($this->allowedActions[$i]); } diff --git a/app/vendor/cakephp/cakephp/src/Controller/Component/CookieComponent.php b/app/vendor/cakephp/cakephp/src/Controller/Component/CookieComponent.php index 3f6ded2bf..795350c9e 100644 --- a/app/vendor/cakephp/cakephp/src/Controller/Component/CookieComponent.php +++ b/app/vendor/cakephp/cakephp/src/Controller/Component/CookieComponent.php @@ -15,6 +15,7 @@ namespace Cake\Controller\Component; use Cake\Controller\Component; +use Cake\Http\Cookie\Cookie; use Cake\Http\ServerRequestFactory; use Cake\I18n\Time; use Cake\Utility\CookieCryptTrait; @@ -310,14 +311,18 @@ protected function _write($name, $value) $expires = new Time($config['expires']); $controller = $this->getController(); - $controller->response = $controller->response->withCookie($name, [ - 'value' => $this->_encrypt($value, $config['encryption'], $config['key']), - 'expire' => $expires->format('U'), - 'path' => $config['path'], - 'domain' => $config['domain'], - 'secure' => (bool)$config['secure'], - 'httpOnly' => (bool)$config['httpOnly'] - ]); + + $cookie = new Cookie( + $name, + $this->_encrypt($value, $config['encryption'], $config['key']), + $expires, + $config['path'], + $config['domain'], + (bool)$config['secure'], + (bool)$config['httpOnly'] + ); + + $controller->response = $controller->response->withCookie($cookie); } /** @@ -335,14 +340,17 @@ protected function _delete($name) $expires = new Time('now'); $controller = $this->getController(); - $controller->response = $controller->response->withCookie($name, [ - 'value' => '', - 'expire' => (int)$expires->format('U') - 42000, - 'path' => $config['path'], - 'domain' => $config['domain'], - 'secure' => $config['secure'], - 'httpOnly' => $config['httpOnly'] - ]); + $cookie = new Cookie( + $name, + '', + $expires, + $config['path'], + $config['domain'], + (bool)$config['secure'], + (bool)$config['httpOnly'] + ); + + $controller->response = $controller->response->withExpiredCookie($cookie); } /** diff --git a/app/vendor/cakephp/cakephp/src/Controller/Component/CsrfComponent.php b/app/vendor/cakephp/cakephp/src/Controller/Component/CsrfComponent.php index e09c8a918..5301b4da9 100644 --- a/app/vendor/cakephp/cakephp/src/Controller/Component/CsrfComponent.php +++ b/app/vendor/cakephp/cakephp/src/Controller/Component/CsrfComponent.php @@ -16,6 +16,7 @@ use Cake\Controller\Component; use Cake\Event\Event; +use Cake\Http\Cookie\Cookie; use Cake\Http\Exception\InvalidCsrfTokenException; use Cake\Http\Response; use Cake\Http\ServerRequest; @@ -61,6 +62,20 @@ class CsrfComponent extends Component 'field' => '_csrfToken', ]; + /** + * Warn if CsrfComponent is used together with CsrfProtectionMiddleware + * + * @param array $config The config data. + * @return void + */ + public function initialize(array $config) + { + if ($this->getController()->getRequest()->getParam('_csrfToken') !== false) { + deprecationWarning('Loading CsrfComponent while CsrfProtectionMiddleware is active ' . + 'will corrupt CSRF data and form submitting will fail.'); + } + } + /** * Startup callback. * @@ -134,13 +149,18 @@ protected function _setCookie(ServerRequest $request, Response $response) $value = hash('sha512', Security::randomBytes(16), false); $request = $request->withParam('_csrfToken', $value); - $response = $response->withCookie($this->_config['cookieName'], [ - 'value' => $value, - 'expire' => $expiry->format('U'), - 'path' => $request->getAttribute('webroot'), - 'secure' => $this->_config['secure'], - 'httpOnly' => $this->_config['httpOnly'], - ]); + + $cookie = new Cookie( + $this->_config['cookieName'], + $value, + $expiry, + $request->getAttribute('webroot'), + '', + (bool)$this->_config['secure'], + (bool)$this->_config['httpOnly'] + ); + + $response = $response->withCookie($cookie); return [$request, $response]; } diff --git a/app/vendor/cakephp/cakephp/src/Controller/Component/FlashComponent.php b/app/vendor/cakephp/cakephp/src/Controller/Component/FlashComponent.php index e05d868d6..62bf7bd5f 100644 --- a/app/vendor/cakephp/cakephp/src/Controller/Component/FlashComponent.php +++ b/app/vendor/cakephp/cakephp/src/Controller/Component/FlashComponent.php @@ -35,6 +35,7 @@ class FlashComponent extends Component * The Session object instance * * @var \Cake\Http\Session + * @deprecated 3.7.5 This property will be removed in 4.0.0 in favor of `getSession()` method. */ protected $_session; @@ -86,7 +87,7 @@ public function __construct(ComponentRegistry $registry, array $config = []) */ public function set($message, array $options = []) { - $options += $this->getConfig(); + $options += (array)$this->getConfig(); if ($message instanceof Exception) { if (!isset($options['params']['code'])) { @@ -109,7 +110,7 @@ public function set($message, array $options = []) $messages = []; if (!$options['clear']) { - $messages = (array)$this->_session->read('Flash.' . $options['key']); + $messages = (array)$this->getSession()->read('Flash.' . $options['key']); } if (!$options['duplicate']) { @@ -127,7 +128,7 @@ public function set($message, array $options = []) 'params' => $options['params'] ]; - $this->_session->write('Flash.' . $options['key'], $messages); + $this->getSession()->write('Flash.' . $options['key'], $messages); } /** @@ -172,4 +173,14 @@ public function __call($name, $args) $this->set($args[0], $options); } + + /** + * Returns current session object from a controller request. + * + * @return \Cake\Http\Session + */ + protected function getSession() + { + return $this->getController()->getRequest()->getSession(); + } } diff --git a/app/vendor/cakephp/cakephp/src/Controller/Component/PaginatorComponent.php b/app/vendor/cakephp/cakephp/src/Controller/Component/PaginatorComponent.php index e44a36bc5..e8b769f06 100644 --- a/app/vendor/cakephp/cakephp/src/Controller/Component/PaginatorComponent.php +++ b/app/vendor/cakephp/cakephp/src/Controller/Component/PaginatorComponent.php @@ -30,6 +30,7 @@ * You configure pagination when calling paginate(). See that method for more details. * * @link https://book.cakephp.org/3.0/en/controllers/components/pagination.html + * @mixin \Cake\Datasource\Paginator */ class PaginatorComponent extends Component { diff --git a/app/vendor/cakephp/cakephp/src/Controller/Component/RequestHandlerComponent.php b/app/vendor/cakephp/cakephp/src/Controller/Component/RequestHandlerComponent.php index f0ad7d279..85482689a 100644 --- a/app/vendor/cakephp/cakephp/src/Controller/Component/RequestHandlerComponent.php +++ b/app/vendor/cakephp/cakephp/src/Controller/Component/RequestHandlerComponent.php @@ -21,6 +21,7 @@ use Cake\Core\Configure; use Cake\Core\Exception\Exception; use Cake\Event\Event; +use Cake\Http\Exception\NotFoundException; use Cake\Http\Response; use Cake\Routing\Router; use Cake\Utility\Exception\XmlException; @@ -49,6 +50,8 @@ class RequestHandlerComponent extends Component /** * Contains the file extension parsed out by the Router * + * Deprecated as of 3.7.0. This property will be made protected in 4.0.0. + * * @var string|null * @see \Cake\Routing\Router::extensions() */ @@ -238,7 +241,7 @@ public function convertXml($xml) try { $xml = Xml::build($xml, ['return' => 'domdocument', 'readFile' => false]); // We might not get child nodes if there are nested inline entities. - if ($xml->childNodes->length > 0) { + if ((int)$xml->childNodes->length > 0) { return Xml::toArray($xml); } @@ -320,6 +323,7 @@ public function beforeRedirect(Event $event, $url, Response $response) * * @param \Cake\Event\Event $event The Controller.beforeRender event. * @return bool false if the render process should be aborted + * @throws \Cake\Http\Exception\NotFoundException If invoked extension is not configured. */ public function beforeRender(Event $event) { @@ -328,12 +332,11 @@ public function beforeRender(Event $event) $response = $controller->getResponse(); $request = $controller->getRequest(); - $isRecognized = ( - !in_array($this->ext, ['html', 'htm']) && - $response->getMimeType($this->ext) - ); + if ($this->ext && !in_array($this->ext, ['html', 'htm'])) { + if (!$response->getMimeType($this->ext)) { + throw new NotFoundException('Invoked extension not recognized/configured: ' . $this->ext); + } - if ($this->ext && $isRecognized) { $this->renderAs($controller, $this->ext); $response = $controller->response; } else { @@ -354,9 +357,14 @@ public function beforeRender(Event $event) * Returns true if the current call accepts an XML response, false otherwise * * @return bool True if client accepts an XML response + * @deprecated 3.7.0 Use RequestHandler::prefers('xml') instead. */ public function isXml() { + deprecationWarning( + 'RequestHandlerComponent::isXml() is deprecated. Use RequestHandlerComponent::prefers(\'xml\') instead.' + ); + return $this->prefers('xml'); } @@ -364,19 +372,29 @@ public function isXml() * Returns true if the current call accepts an RSS response, false otherwise * * @return bool True if client accepts an RSS response + * @deprecated 3.7.0 Use RequestHandler::prefers('rss') instead. */ public function isRss() { + deprecationWarning( + 'RequestHandlerComponent::isRss() is deprecated. Use RequestHandlerComponent::prefers(\'rss\') instead.' + ); + return $this->prefers('rss'); } /** * Returns true if the current call accepts an Atom response, false otherwise * - * @return bool True if client accepts an RSS response + * @return bool True if client accepts an Atom response + * @deprecated 3.7.0 Use RequestHandler::prefers('atom') instead. */ public function isAtom() { + deprecationWarning( + 'RequestHandlerComponent::isAtom() is deprecated. Use RequestHandlerComponent::prefers(\'atom\') instead.' + ); + return $this->prefers('atom'); } @@ -385,9 +403,14 @@ public function isAtom() * client accepts WAP content. * * @return bool True if user agent is a mobile web browser + * @deprecated 3.7.0 Use ServerRequest::is('mobile') instead. */ public function isMobile() { + deprecationWarning( + 'RequestHandlerComponent::isMobile() is deprecated. Use ServerRequest::is(\'mobile\') instead.' + ); + $request = $this->getController()->getRequest(); return $request->is('mobile') || $this->accepts('wap'); @@ -397,9 +420,14 @@ public function isMobile() * Returns true if the client accepts WAP content * * @return bool + * @deprecated 3.7.0 Use RequestHandler::prefers('wap') instead. */ public function isWap() { + deprecationWarning( + 'RequestHandlerComponent::isWap() is deprecated. Use RequestHandlerComponent::prefers(\'wap\') instead.' + ); + return $this->prefers('wap'); } @@ -679,9 +707,14 @@ public function respondAs($type, array $options = []) * * @return mixed A string content type alias, or raw content type if no alias map exists, * otherwise null + * @deprecated 3.7.0 Use $response->mapType($response->getType()) instead. */ public function responseType() { + deprecationWarning( + 'RequestHandlerComponent::responseType() is deprecated. Use $response->mapType($response->getType()) instead.' + ); + $response = $this->getController()->response; return $response->mapType($response->getType()); diff --git a/app/vendor/cakephp/cakephp/src/Controller/Component/SecurityComponent.php b/app/vendor/cakephp/cakephp/src/Controller/Component/SecurityComponent.php index e3ca37057..d59efb3a6 100644 --- a/app/vendor/cakephp/cakephp/src/Controller/Component/SecurityComponent.php +++ b/app/vendor/cakephp/cakephp/src/Controller/Component/SecurityComponent.php @@ -121,10 +121,10 @@ public function startup(Event $event) $isNotRequestAction && $this->_config['validatePost'] ) { - $this->_validatePost($controller); + $this->_validatePost($controller); } } catch (SecurityException $se) { - $this->blackHole($controller, $se->getType(), $se); + return $this->blackHole($controller, $se->getType(), $se); } $request = $this->generateToken($request); diff --git a/app/vendor/cakephp/cakephp/src/Controller/Controller.php b/app/vendor/cakephp/cakephp/src/Controller/Controller.php index 9dc8e6ddf..df07d6bac 100644 --- a/app/vendor/cakephp/cakephp/src/Controller/Controller.php +++ b/app/vendor/cakephp/cakephp/src/Controller/Controller.php @@ -205,6 +205,7 @@ class Controller implements EventListenerInterface, EventDispatcherInterface * * @var array * @see \Cake\View\View + * @deprecated 3.7.0 Use ViewBuilder::setOptions() or any one of it's setter methods instead. */ protected $_validViewOptions = [ 'passedArgs' @@ -370,11 +371,24 @@ public function __get($name) } list($plugin, $class) = pluginSplit($this->modelClass, true); - if ($class !== $name) { - return false; - } + if ($class === $name) { + return $this->loadModel($plugin . $class); + } + + $trace = debug_backtrace(); + $parts = explode('\\', get_class($this)); + trigger_error( + sprintf( + 'Undefined property: %s::$%s in %s on line %s', + array_pop($parts), + $name, + $trace[0]['file'], + $trace[0]['line'] + ), + E_USER_NOTICE + ); - return $this->loadModel($plugin . $class); + return false; } /** @@ -593,7 +607,12 @@ public function invokeAction() /* @var callable $callable */ $callable = [$this, $request->getParam('action')]; - return $callable(...array_values($request->getParam('pass'))); + $result = $callable(...array_values($request->getParam('pass'))); + if ($result instanceof Response) { + $this->response = $result; + } + + return $result; } /** @@ -757,7 +776,7 @@ public function render($view = null, $layout = null) } if ($this->request->getParam('bare')) { - $builder->enableAutoLayout(false); + $builder->disableAutoLayout(); } $this->autoRender = false; @@ -775,7 +794,7 @@ public function render($view = null, $layout = null) $this->View = $this->createView(); $contents = $this->View->render($view, $layout); - $this->response = $this->View->response->withStringBody($contents); + $this->setResponse($this->View->getResponse()->withStringBody($contents)); return $this->response; } diff --git a/app/vendor/cakephp/cakephp/src/Core/Configure.php b/app/vendor/cakephp/cakephp/src/Core/Configure.php index cb65453ce..ae3dbf481 100644 --- a/app/vendor/cakephp/cakephp/src/Core/Configure.php +++ b/app/vendor/cakephp/cakephp/src/Core/Configure.php @@ -260,18 +260,37 @@ public static function config($name, ConfigEngineInterface $engine) /** * Gets the names of the configured Engine objects. * + * Checking if a specific engine has been configured with this method is deprecated. + * Use Configure::isConfigured() instead. + * * @param string|null $name Engine name. - * @return array|bool Array of the configured Engine objects, bool for specific name. + * @return string[]|bool Array of the configured Engine objects, bool for specific name. */ public static function configured($name = null) { if ($name !== null) { + deprecationWarning( + 'Checking for a named engine with configured() is deprecated. ' . + 'Use Configure::isConfigured() instead.' + ); + return isset(static::$_engines[$name]); } return array_keys(static::$_engines); } + /** + * Returns true if the Engine objects is configured. + * + * @param string $name Engine name. + * @return bool + */ + public static function isConfigured($name) + { + return isset(static::$_engines[$name]); + } + /** * Remove a configured engine. This will unset the engine * and make any future attempts to use it cause an Exception. @@ -354,7 +373,7 @@ public static function load($key, $config = 'default', $merge = true) * @param string $key The identifier to create in the config adapter. * This could be a filename or a cache key depending on the adapter being used. * @param string $config The name of the configured adapter to dump data with. - * @param array $keys The name of the top-level keys you want to dump. + * @param string[] $keys The name of the top-level keys you want to dump. * This allows you save only some data stored in Configure. * @return bool Success * @throws \Cake\Core\Exception\Exception if the adapter does not implement a `dump` method. @@ -427,6 +446,9 @@ public static function store($name, $cacheConfig = 'default', $data = null) if ($data === null) { $data = static::$_values; } + if (!class_exists(Cache::class)) { + throw new RuntimeException('You must install cakephp/cache to use Configure::store()'); + } return Cache::write($name, $data, $cacheConfig); } @@ -441,6 +463,9 @@ public static function store($name, $cacheConfig = 'default', $data = null) */ public static function restore($name, $cacheConfig = 'default') { + if (!class_exists(Cache::class)) { + throw new RuntimeException('You must install cakephp/cache to use Configure::restore()'); + } $values = Cache::read($name, $cacheConfig); if ($values) { return static::write($values); diff --git a/app/vendor/cakephp/cakephp/src/Core/Configure/Engine/PhpConfig.php b/app/vendor/cakephp/cakephp/src/Core/Configure/Engine/PhpConfig.php index ef4cae491..2cc5eab7a 100644 --- a/app/vendor/cakephp/cakephp/src/Core/Configure/Engine/PhpConfig.php +++ b/app/vendor/cakephp/cakephp/src/Core/Configure/Engine/PhpConfig.php @@ -85,12 +85,14 @@ public function read($key) { $file = $this->_getFilePath($key, true); + $config = null; + $return = include $file; if (is_array($return)) { return $return; } - if (!isset($config)) { + if ($config === null) { throw new Exception(sprintf('Config file "%s" did not return an array', $key . '.php')); } deprecationWarning(sprintf( diff --git a/app/vendor/cakephp/cakephp/src/Core/ConventionsTrait.php b/app/vendor/cakephp/cakephp/src/Core/ConventionsTrait.php index 28458302a..2ca06f0a9 100644 --- a/app/vendor/cakephp/cakephp/src/Core/ConventionsTrait.php +++ b/app/vendor/cakephp/cakephp/src/Core/ConventionsTrait.php @@ -30,7 +30,7 @@ trait ConventionsTrait */ protected function _fixtureName($name) { - return Inflector::underscore($name); + return Inflector::camelize($name); } /** @@ -135,7 +135,7 @@ protected function _pluralHumanName($name) */ protected function _pluginPath($pluginName) { - if (Plugin::loaded($pluginName)) { + if (Plugin::isLoaded($pluginName)) { return Plugin::path($pluginName); } diff --git a/app/vendor/cakephp/cakephp/src/Core/InstanceConfigTrait.php b/app/vendor/cakephp/cakephp/src/Core/InstanceConfigTrait.php index bea302f90..dd1acc889 100644 --- a/app/vendor/cakephp/cakephp/src/Core/InstanceConfigTrait.php +++ b/app/vendor/cakephp/cakephp/src/Core/InstanceConfigTrait.php @@ -111,7 +111,7 @@ public function setConfig($key, $value = null, $merge = true) * * @param string|null $key The key to get or null for the whole config. * @param mixed $default The return value when the key does not exist. - * @return mixed Config value being read. + * @return mixed Configuration data at the named key or null if the key does not exist. */ public function getConfig($key = null, $default = null) { diff --git a/app/vendor/cakephp/cakephp/src/Core/ObjectRegistry.php b/app/vendor/cakephp/cakephp/src/Core/ObjectRegistry.php index 2b72bb9b0..1a85a0abe 100644 --- a/app/vendor/cakephp/cakephp/src/Core/ObjectRegistry.php +++ b/app/vendor/cakephp/cakephp/src/Core/ObjectRegistry.php @@ -120,7 +120,7 @@ protected function _checkDuplicate($name, $config) { /** @var \Cake\Core\InstanceConfigTrait $existing */ $existing = $this->_loaded[$name]; - $msg = sprintf('The "%s" alias has already been loaded', $name); + $msg = sprintf('The "%s" alias has already been loaded.', $name); $hasConfig = method_exists($existing, 'config'); if (!$hasConfig) { throw new RuntimeException($msg); @@ -131,22 +131,24 @@ protected function _checkDuplicate($name, $config) $existingConfig = $existing->getConfig(); unset($config['enabled'], $existingConfig['enabled']); - $fail = false; + $failure = null; foreach ($config as $key => $value) { if (!array_key_exists($key, $existingConfig)) { - $fail = true; + $failure = " The `{$key}` was not defined in the previous configuration data."; break; } if (isset($existingConfig[$key]) && $existingConfig[$key] !== $value) { - $fail = true; + $failure = sprintf( + ' The `%s` key has a value of `%s` but previously had a value of `%s`', + $key, + json_encode($value), + json_encode($existingConfig[$key]) + ); break; } } - if ($fail) { - $msg .= ' with the following config: '; - $msg .= var_export($existingConfig, true); - $msg .= ' which differs from ' . var_export($config, true); - throw new RuntimeException($msg); + if ($failure) { + throw new RuntimeException($msg . $failure); } } @@ -184,7 +186,7 @@ abstract protected function _create($class, $alias, $config); /** * Get the list of loaded objects. * - * @return array List of object names. + * @return string[] List of object names. */ public function loaded() { diff --git a/app/vendor/cakephp/cakephp/src/Core/Plugin.php b/app/vendor/cakephp/cakephp/src/Core/Plugin.php index 70f426298..2068d37bb 100644 --- a/app/vendor/cakephp/cakephp/src/Core/Plugin.php +++ b/app/vendor/cakephp/cakephp/src/Core/Plugin.php @@ -108,9 +108,16 @@ class Plugin * @param array $config configuration options for the plugin * @throws \Cake\Core\Exception\MissingPluginException if the folder for the plugin to be loaded is not found * @return void + * @deprecated 3.7.0 This method will be removed in 4.0.0. Use Application::addPlugin() instead. */ public static function load($plugin, array $config = []) { + deprecationWarning( + 'Plugin::load() is deprecated. ' . + 'Use Application::addPlugin() instead. ' . + 'This method will be removed in 4.0.0.' + ); + if (is_array($plugin)) { foreach ($plugin as $name => $conf) { list($name, $conf) = is_numeric($name) ? [$conf, $config] : [$name, $conf]; @@ -188,6 +195,7 @@ public static function load($plugin, array $config = []) * @param array $options Options. * @return void * @throws \Cake\Core\Exception\MissingPluginException + * @deprecated 3.7.0 This method will be removed in 4.0.0. */ public static function loadAll(array $options = []) { @@ -269,9 +277,14 @@ public static function configPath($name) * @param string $name name of the plugin * @return mixed * @see \Cake\Core\Plugin::load() for examples of bootstrap configuration + * @deprecated 3.7.0 This method will be removed in 4.0.0. */ public static function bootstrap($name) { + deprecationWarning( + 'Plugin::bootstrap() is deprecated. ' . + 'This method will be removed in 4.0.0.' + ); $plugin = static::getCollection()->get($name); if (!$plugin->isEnabled('bootstrap')) { return false; @@ -324,8 +337,23 @@ public static function routes($name = null) } /** - * Returns true if the plugin $plugin is already loaded - * If plugin is null, it will return a list of all loaded plugins + * Check whether or not a plugin is loaded. + * + * @param string $plugin The name of the plugin to check. + * @return bool + * @since 3.7.0 + */ + public static function isLoaded($plugin) + { + return static::getCollection()->has($plugin); + } + + /** + * Return a list of loaded plugins. + * + * If a plugin name is provided, the return value will be a bool + * indicating whether or not the named plugin is loaded. This usage + * is deprecated. Instead you should use Plugin::isLoaded($name) * * @param string|null $plugin Plugin name. * @return bool|array Boolean true if $plugin is already loaded. @@ -334,6 +362,11 @@ public static function routes($name = null) public static function loaded($plugin = null) { if ($plugin !== null) { + deprecationWarning( + 'Checking a single plugin with Plugin::loaded() is deprecated. ' . + 'Use Plugin::isLoaded() instead.' + ); + return static::getCollection()->has($plugin); } $names = []; @@ -349,12 +382,14 @@ public static function loaded($plugin = null) * Forgets a loaded plugin or all of them if first parameter is null * * @param string|null $plugin name of the plugin to forget + * @deprecated 3.7.0 This method will be removed in 4.0.0. Use PluginCollection::remove() or clear() instead. * @return void */ public static function unload($plugin = null) { + deprecationWarning('Plugin::unload() will be removed in 4.0. Use PluginCollection::remove() or clear()'); if ($plugin === null) { - static::$plugins = null; + static::getCollection()->clear(); } else { static::getCollection()->remove($plugin); } @@ -379,7 +414,9 @@ protected static function _includeFile($file, $ignoreMissing = false) /** * Get the shared plugin collection. * - * @internal + * This method should generally not be used during application + * runtime as plugins should be set during Application startup. + * * @return \Cake\Core\PluginCollection */ public static function getCollection() diff --git a/app/vendor/cakephp/cakephp/src/Core/PluginCollection.php b/app/vendor/cakephp/cakephp/src/Core/PluginCollection.php index dce99e098..351dee105 100644 --- a/app/vendor/cakephp/cakephp/src/Core/PluginCollection.php +++ b/app/vendor/cakephp/cakephp/src/Core/PluginCollection.php @@ -28,6 +28,9 @@ * This class implements the Iterator interface to allow plugins * to be iterated, handling the situation where a plugin's hook * method (usually bootstrap) loads another plugin during iteration. + * + * While its implementation supported nested iteration it does not + * support using `continue` or `break` inside loops. */ class PluginCollection implements Iterator, Countable { @@ -46,11 +49,18 @@ class PluginCollection implements Iterator, Countable protected $names = []; /** - * Iterator position. + * Iterator position stack. + * + * @var int[] + */ + protected $positions = []; + + /** + * Loop depth * * @var int */ - protected $position = 0; + protected $loopDepth = -1; /** * Constructor @@ -158,6 +168,21 @@ public function remove($name) return $this; } + /** + * Remove all plugins from the collection + * + * @return $this + */ + public function clear() + { + $this->plugins = []; + $this->names = []; + $this->positions = []; + $this->loopDepth = -1; + + return $this; + } + /** * Check whether the named plugin exists in the collection. * @@ -185,6 +210,18 @@ public function get($name) return $this->plugins[$name]; } + /** + * Implementation of Countable. + * + * Get the number of plugins in the collection. + * + * @return int + */ + public function count() + { + return count($this->plugins); + } + /** * Part of Iterator Interface * @@ -192,7 +229,7 @@ public function get($name) */ public function next() { - $this->position++; + $this->positions[$this->loopDepth]++; } /** @@ -202,7 +239,7 @@ public function next() */ public function key() { - return $this->names[$this->position]; + return $this->names[$this->positions[$this->loopDepth]]; } /** @@ -212,7 +249,8 @@ public function key() */ public function current() { - $name = $this->names[$this->position]; + $position = $this->positions[$this->loopDepth]; + $name = $this->names[$position]; return $this->plugins[$name]; } @@ -224,7 +262,8 @@ public function current() */ public function rewind() { - $this->position = 0; + $this->positions[] = 0; + $this->loopDepth += 1; } /** @@ -234,19 +273,13 @@ public function rewind() */ public function valid() { - return $this->position < count($this->plugins); - } + $valid = isset($this->names[$this->positions[$this->loopDepth]]); + if (!$valid) { + array_pop($this->positions); + $this->loopDepth -= 1; + } - /** - * Implementation of Countable. - * - * Get the number of plugins in the collection. - * - * @return int - */ - public function count() - { - return count($this->plugins); + return $valid; } /** diff --git a/app/vendor/cakephp/cakephp/src/Core/StaticConfigTrait.php b/app/vendor/cakephp/cakephp/src/Core/StaticConfigTrait.php index feb49962c..e06f61450 100644 --- a/app/vendor/cakephp/cakephp/src/Core/StaticConfigTrait.php +++ b/app/vendor/cakephp/cakephp/src/Core/StaticConfigTrait.php @@ -110,7 +110,7 @@ public static function setConfig($key, $config = null) * Reads existing configuration. * * @param string $key The name of the configuration. - * @return array|null Array of configuration data. + * @return mixed Configuration data at the named key or null if the key does not exist. */ public static function getConfig($key) { @@ -205,7 +205,7 @@ public static function drop($config) /** * Returns an array containing the named configurations * - * @return array Array of configurations. + * @return string[] Array of configurations. */ public static function configured() { diff --git a/app/vendor/cakephp/cakephp/src/Core/composer.json b/app/vendor/cakephp/cakephp/src/Core/composer.json index 3a485859c..0ee9afc93 100644 --- a/app/vendor/cakephp/cakephp/src/Core/composer.json +++ b/app/vendor/cakephp/cakephp/src/Core/composer.json @@ -26,7 +26,8 @@ "cakephp/utility": "^3.6.0" }, "suggest": { - "cakephp/event": "To use PluginApplicationInterface or plugin applications." + "cakephp/event": "To use PluginApplicationInterface or plugin applications.", + "cakephp/cache": "To use Configure::store() and restore()." }, "autoload": { "psr-4": { diff --git a/app/vendor/cakephp/cakephp/src/Core/functions.php b/app/vendor/cakephp/cakephp/src/Core/functions.php index b2ccfd7c7..87573649f 100644 --- a/app/vendor/cakephp/cakephp/src/Core/functions.php +++ b/app/vendor/cakephp/cakephp/src/Core/functions.php @@ -69,6 +69,7 @@ function h($text, $double = true, $charset = null) 'Use the 3rd argument instead.' ); $charset = $double; + $double = true; } return htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, $charset ?: $defaultCharset, $double); diff --git a/app/vendor/cakephp/cakephp/src/Database/Connection.php b/app/vendor/cakephp/cakephp/src/Database/Connection.php index 62a741139..313ca5e71 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Connection.php +++ b/app/vendor/cakephp/cakephp/src/Database/Connection.php @@ -120,7 +120,7 @@ public function __construct($config) $this->setDriver($driver, $config); if (!empty($config['log'])) { - $this->logQueries($config['log']); + $this->enableQueryLogging($config['log']); } } @@ -598,6 +598,18 @@ public function enableSavePoints($enable) return $this; } + /** + * Disables the usage of savepoints. + * + * @return $this + */ + public function disableSavePoints() + { + $this->_useSavePoints = false; + + return $this; + } + /** * Returns whether this connection is using savepoints for nested transactions * @@ -847,15 +859,56 @@ public function cacheMetadata($cache) /** * {@inheritDoc} + * + * @deprecated 3.7.0 Use enableQueryLogging() and isQueryLoggingEnabled() instead. */ public function logQueries($enable = null) { + deprecationWarning( + 'Connection::logQueries() is deprecated. ' . + 'Use enableQueryLogging() and isQueryLoggingEnabled() instead.' + ); if ($enable === null) { return $this->_logQueries; } $this->_logQueries = $enable; } + /** + * Enable/disable query logging + * + * @param bool $value Enable/disable query logging + * @return $this + */ + public function enableQueryLogging($value) + { + $this->_logQueries = (bool)$value; + + return $this; + } + + /** + * Disable query logging + * + * @return $this + */ + public function disableQueryLogging() + { + $this->_logQueries = false; + + return $this; + } + + /** + * Check if query logging is enabled. + * + * @return bool + */ + public function isQueryLoggingEnabled() + { + return $this->_logQueries; + } + /** * {@inheritDoc} * diff --git a/app/vendor/cakephp/cakephp/src/Database/Dialect/PostgresDialectTrait.php b/app/vendor/cakephp/cakephp/src/Database/Dialect/PostgresDialectTrait.php index 3f3e5f4bf..8e5cb7edb 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Dialect/PostgresDialectTrait.php +++ b/app/vendor/cakephp/cakephp/src/Database/Dialect/PostgresDialectTrait.php @@ -132,6 +132,9 @@ protected function _transformFunctionExpression(FunctionExpression $expression) case 'NOW': $expression->setName('LOCALTIMESTAMP')->add([' 0 ' => 'literal']); break; + case 'RAND': + $expression->setName('RANDOM'); + break; case 'DATE_ADD': $expression ->setName('') diff --git a/app/vendor/cakephp/cakephp/src/Database/Dialect/SqliteDialectTrait.php b/app/vendor/cakephp/cakephp/src/Database/Dialect/SqliteDialectTrait.php index 24550909c..173ed362c 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Dialect/SqliteDialectTrait.php +++ b/app/vendor/cakephp/cakephp/src/Database/Dialect/SqliteDialectTrait.php @@ -108,6 +108,11 @@ protected function _transformFunctionExpression(FunctionExpression $expression) case 'NOW': $expression->setName('DATETIME')->add(["'now'" => 'literal']); break; + case 'RAND': + $expression + ->setName('ABS') + ->add(["RANDOM() % 1" => 'literal'], [], true); + break; case 'CURRENT_DATE': $expression->setName('DATE')->add(["'now'" => 'literal']); break; diff --git a/app/vendor/cakephp/cakephp/src/Database/Dialect/TupleComparisonTranslatorTrait.php b/app/vendor/cakephp/cakephp/src/Database/Dialect/TupleComparisonTranslatorTrait.php index a9627b3b6..6c383a0ca 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Dialect/TupleComparisonTranslatorTrait.php +++ b/app/vendor/cakephp/cakephp/src/Database/Dialect/TupleComparisonTranslatorTrait.php @@ -31,7 +31,7 @@ trait TupleComparisonTranslatorTrait * Receives a TupleExpression and changes it so that it conforms to this * SQL dialect. * - * It transforms expressions looking like '(a, b) IN ((c, d), (e, f)' into an + * It transforms expressions looking like '(a, b) IN ((c, d), (e, f))' into an * equivalent expression of the form '((a = c) AND (b = d)) OR ((a = e) AND (b = f))'. * * It can also transform transform expressions where the right hand side is a query diff --git a/app/vendor/cakephp/cakephp/src/Database/Driver.php b/app/vendor/cakephp/cakephp/src/Database/Driver.php index 3cc0bb613..73dab9ac3 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Driver.php +++ b/app/vendor/cakephp/cakephp/src/Database/Driver.php @@ -362,6 +362,18 @@ public function enableAutoQuoting($enable = true) return $this; } + /** + * Disable auto quoting of identifiers in queries. + * + * @return $this + */ + public function disableAutoQuoting() + { + $this->_autoQuoting = false; + + return $this; + } + /** * {@inheritDoc} */ diff --git a/app/vendor/cakephp/cakephp/src/Database/Driver/Mysql.php b/app/vendor/cakephp/cakephp/src/Database/Driver/Mysql.php index 7a9f11ee3..c821a4925 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Driver/Mysql.php +++ b/app/vendor/cakephp/cakephp/src/Database/Driver/Mysql.php @@ -122,7 +122,7 @@ public function connect() */ public function enabled() { - return in_array('mysql', PDO::getAvailableDrivers()); + return in_array('mysql', PDO::getAvailableDrivers(), true); } /** diff --git a/app/vendor/cakephp/cakephp/src/Database/Driver/Postgres.php b/app/vendor/cakephp/cakephp/src/Database/Driver/Postgres.php index 2a2fae8fa..393672ab9 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Driver/Postgres.php +++ b/app/vendor/cakephp/cakephp/src/Database/Driver/Postgres.php @@ -95,7 +95,7 @@ public function connect() */ public function enabled() { - return in_array('pgsql', PDO::getAvailableDrivers()); + return in_array('pgsql', PDO::getAvailableDrivers(), true); } /** diff --git a/app/vendor/cakephp/cakephp/src/Database/Driver/Sqlite.php b/app/vendor/cakephp/cakephp/src/Database/Driver/Sqlite.php index 9b518e687..e53078757 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Driver/Sqlite.php +++ b/app/vendor/cakephp/cakephp/src/Database/Driver/Sqlite.php @@ -91,7 +91,7 @@ public function connect() */ public function enabled() { - return in_array('sqlite', PDO::getAvailableDrivers()); + return in_array('sqlite', PDO::getAvailableDrivers(), true); } /** diff --git a/app/vendor/cakephp/cakephp/src/Database/Driver/Sqlserver.php b/app/vendor/cakephp/cakephp/src/Database/Driver/Sqlserver.php index 74d72fc5e..cb04ef7ad 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Driver/Sqlserver.php +++ b/app/vendor/cakephp/cakephp/src/Database/Driver/Sqlserver.php @@ -132,7 +132,7 @@ public function connect() */ public function enabled() { - return in_array('sqlsrv', PDO::getAvailableDrivers()); + return in_array('sqlsrv', PDO::getAvailableDrivers(), true); } /** diff --git a/app/vendor/cakephp/cakephp/src/Database/DriverInterface.php b/app/vendor/cakephp/cakephp/src/Database/DriverInterface.php index 3ac4b64cb..9316df18f 100644 --- a/app/vendor/cakephp/cakephp/src/Database/DriverInterface.php +++ b/app/vendor/cakephp/cakephp/src/Database/DriverInterface.php @@ -18,6 +18,8 @@ /** * Interface for database driver. + * + * @method $this disableAutoQuoting() */ interface DriverInterface { diff --git a/app/vendor/cakephp/cakephp/src/Database/Expression/Comparison.php b/app/vendor/cakephp/cakephp/src/Database/Expression/Comparison.php index 25147e39f..751fc6b65 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Expression/Comparison.php +++ b/app/vendor/cakephp/cakephp/src/Database/Expression/Comparison.php @@ -195,7 +195,7 @@ public function traverse(callable $callable) public function __clone() { foreach (['_value', '_field'] as $prop) { - if ($prop instanceof ExpressionInterface) { + if ($this->{$prop} instanceof ExpressionInterface) { $this->{$prop} = clone $this->{$prop}; } } diff --git a/app/vendor/cakephp/cakephp/src/Database/Expression/FunctionExpression.php b/app/vendor/cakephp/cakephp/src/Database/Expression/FunctionExpression.php index 258b36801..e569a369e 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Expression/FunctionExpression.php +++ b/app/vendor/cakephp/cakephp/src/Database/Expression/FunctionExpression.php @@ -15,6 +15,7 @@ namespace Cake\Database\Expression; use Cake\Database\ExpressionInterface; +use Cake\Database\Query; use Cake\Database\TypedResultInterface; use Cake\Database\TypedResultTrait; use Cake\Database\Type\ExpressionTypeCasterTrait; @@ -171,8 +172,10 @@ public function sql(ValueBinder $generator) { $parts = []; foreach ($this->_conditions as $condition) { - if ($condition instanceof ExpressionInterface) { - $condition = sprintf('%s', $condition->sql($generator)); + if ($condition instanceof Query) { + $condition = sprintf('(%s)', $condition->sql($generator)); + } elseif ($condition instanceof ExpressionInterface) { + $condition = $condition->sql($generator); } elseif (is_array($condition)) { $p = $generator->placeholder('param'); $generator->bind($p, $condition['value'], $condition['type']); diff --git a/app/vendor/cakephp/cakephp/src/Database/Expression/QueryExpression.php b/app/vendor/cakephp/cakephp/src/Database/Expression/QueryExpression.php index 26c63f538..1ac6bdb9a 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Expression/QueryExpression.php +++ b/app/vendor/cakephp/cakephp/src/Database/Expression/QueryExpression.php @@ -25,6 +25,9 @@ * Represents a SQL Query expression. Internally it stores a tree of * expressions that can be compiled by converting this object to string * and will contain a correctly parenthesized and nested expression. + * + * @method $this and(callable|string|array|\Cake\Database\ExpressionInterface $conditions) + * @method $this or(callable|string|array|\Cake\Database\ExpressionInterface $conditions) */ class QueryExpression implements ExpressionInterface, Countable { @@ -449,7 +452,7 @@ public function between($field, $from, $to, $type = null) * Returns a new QueryExpression object containing all the conditions passed * and set up the conjunction to be "AND" * - * @param string|array|\Cake\Database\ExpressionInterface $conditions to be joined with AND + * @param callable|string|array|\Cake\Database\ExpressionInterface $conditions to be joined with AND * @param array $types associative array of fields pointing to the type of the * values that are being passed. Used for correctly binding values to statements. * @return \Cake\Database\Expression\QueryExpression @@ -467,7 +470,7 @@ public function and_($conditions, $types = []) * Returns a new QueryExpression object containing all the conditions passed * and set up the conjunction to be "OR" * - * @param string|array|\Cake\Database\ExpressionInterface $conditions to be joined with OR + * @param callable|string|array|\Cake\Database\ExpressionInterface $conditions to be joined with OR * @param array $types associative array of fields pointing to the type of the * values that are being passed. Used for correctly binding values to statements. * @return \Cake\Database\Expression\QueryExpression diff --git a/app/vendor/cakephp/cakephp/src/Database/FunctionsBuilder.php b/app/vendor/cakephp/cakephp/src/Database/FunctionsBuilder.php index d31664098..22a3b71e1 100644 --- a/app/vendor/cakephp/cakephp/src/Database/FunctionsBuilder.php +++ b/app/vendor/cakephp/cakephp/src/Database/FunctionsBuilder.php @@ -60,6 +60,16 @@ protected function _literalArgumentFunction($name, $expression, $types = [], $re return $this->_build($name, $expression, $types, $return); } + /** + * Returns a FunctionExpression representing a call to SQL RAND function. + * + * @return \Cake\Database\Expression\FunctionExpression + */ + public function rand() + { + return $this->_build('RAND', [], [], 'float'); + } + /** * Returns a FunctionExpression representing a call to SQL SUM function. * diff --git a/app/vendor/cakephp/cakephp/src/Database/Log/LoggingStatement.php b/app/vendor/cakephp/cakephp/src/Database/Log/LoggingStatement.php index 525eb48e3..2fd2dcd99 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Log/LoggingStatement.php +++ b/app/vendor/cakephp/cakephp/src/Database/Log/LoggingStatement.php @@ -78,7 +78,7 @@ public function execute($params = null) */ protected function _log($query, $params, $startTime) { - $query->took = round((microtime(true) - $startTime) * 1000, 0); + $query->took = (int)round((microtime(true) - $startTime) * 1000, 0); $query->params = $params ?: $this->_compiledParams; $query->query = $this->queryString; $this->getLogger()->log($query); diff --git a/app/vendor/cakephp/cakephp/src/Database/Query.php b/app/vendor/cakephp/cakephp/src/Database/Query.php index 4a7b8b9b7..de223a765 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Query.php +++ b/app/vendor/cakephp/cakephp/src/Database/Query.php @@ -299,7 +299,7 @@ public function sql(ValueBinder $generator = null) * ``` * * @param callable $visitor A function or callable to be executed for each part - * @param array $parts The query clauses to traverse + * @param string[] $parts The query clauses to traverse * @return $this */ public function traverse(callable $visitor, array $parts = []) @@ -484,6 +484,8 @@ public function modifier($modifiers, $overwrite = false) public function from($tables = [], $overwrite = false) { if (empty($tables)) { + deprecationWarning('Using Query::from() to read state is deprecated. Use clause("from") instead.'); + return $this->_parts['from']; } @@ -589,6 +591,8 @@ public function from($tables = [], $overwrite = false) public function join($tables = null, $types = [], $overwrite = false) { if ($tables === null) { + deprecationWarning('Using Query::join() to read state is deprecated. Use Query::clause("join") instead.'); + return $this->_parts['join']; } @@ -672,7 +676,7 @@ public function removeJoin($name) * * See `join()` for further details on conditions and types. * - * @param string|array $table The table to join with + * @param string|string[] $table The table to join with * @param string|array|\Cake\Database\ExpressionInterface $conditions The conditions * to use for joining. * @param array $types a list of types associated to the conditions used for converting @@ -712,7 +716,7 @@ public function rightJoin($table, $conditions = [], $types = []) * The arguments of this method are identical to the `leftJoin()` shorthand, please refer * to that methods description for further details. * - * @param string|array $table The table to join with + * @param string|string[] $table The table to join with * @param string|array|\Cake\Database\ExpressionInterface $conditions The conditions * to use for joining. * @param array $types a list of types associated to the conditions used for converting @@ -727,7 +731,7 @@ public function innerJoin($table, $conditions = [], $types = []) /** * Returns an array that can be passed to the join method describing a single join clause * - * @param string|array $table The table to join with + * @param string|string[] $table The table to join with * @param string|array|\Cake\Database\ExpressionInterface $conditions The conditions * to use for joining. * @param string $type the join type to use @@ -1534,7 +1538,7 @@ public function unionAll($query, $overwrite = false) * with Query::values(). * * @param array $columns The columns to insert into. - * @param array $types A map between columns & their datatypes. + * @param string[] $types A map between columns & their datatypes. * @return $this * @throws \RuntimeException When there are 0 columns. */ @@ -1956,17 +1960,10 @@ public function traverseExpressions(callable $callback) /** * Associates a query placeholder to a value and a type. * - * If type is expressed as "atype[]" (note braces) then it will cause the - * placeholder to be re-written dynamically so if the value is an array, it - * will create as many placeholders as values are in it. For example: - * * ``` - * $query->bind(':id', [1, 2, 3], 'int[]'); + * $query->bind(':id', 1, 'integer'); * ``` * - * Will create 3 int placeholders. When using named placeholders, this method - * requires that the placeholders include `:` e.g. `:value`. - * * @param string|int $param placeholder to be replaced with quoted version * of $value * @param mixed $value The value to be bound @@ -2065,6 +2062,22 @@ public function enableBufferedResults($enable = true) return $this; } + /** + * Disables buffered results. + * + * Disabling buffering will consume less memory as fetched results are not + * remembered for future iterations. + * + * @return $this + */ + public function disableBufferedResults() + { + $this->_dirty(); + $this->_useBufferedResults = false; + + return $this; + } + /** * Returns whether buffered results are enabled/disabled. * diff --git a/app/vendor/cakephp/cakephp/src/Database/Schema/Collection.php b/app/vendor/cakephp/cakephp/src/Database/Schema/Collection.php index 483bc9a3b..0222e3c3d 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Schema/Collection.php +++ b/app/vendor/cakephp/cakephp/src/Database/Schema/Collection.php @@ -73,6 +73,8 @@ public function listTables() /** * Get the column metadata for a table. * + * The name can include a database schema name in the form 'schema.table'. + * * Caching will be applied if `cacheMetadata` key is present in the Connection * configuration options. Defaults to _cake_model_ when true. * diff --git a/app/vendor/cakephp/cakephp/src/Database/Schema/MysqlSchema.php b/app/vendor/cakephp/cakephp/src/Database/Schema/MysqlSchema.php index 52d7fe204..ce1d95960 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Schema/MysqlSchema.php +++ b/app/vendor/cakephp/cakephp/src/Database/Schema/MysqlSchema.php @@ -137,7 +137,7 @@ protected function _convertColumn($column) if ($col === 'binary' && $length === 16) { return ['type' => TableSchema::TYPE_BINARY_UUID, 'length' => null]; } - if (strpos($col, 'blob') !== false || $col === 'binary') { + if (strpos($col, 'blob') !== false || in_array($col, ['binary', 'varbinary'])) { $lengthName = substr($col, 0, -4); $length = isset(TableSchema::$columnLengths[$lengthName]) ? TableSchema::$columnLengths[$lengthName] : $length; @@ -359,16 +359,22 @@ public function columnSql(TableSchema $schema, $name) break; case TableSchema::TYPE_BINARY: $isKnownLength = in_array($data['length'], TableSchema::$columnLengths); - if (empty($data['length']) || !$isKnownLength) { - $out .= ' BLOB'; - break; - } - if ($isKnownLength) { $length = array_search($data['length'], TableSchema::$columnLengths); $out .= ' ' . strtoupper($length) . 'BLOB'; + break; } + if (empty($data['length'])) { + $out .= ' BLOB'; + break; + } + + if ($data['length'] > 2) { + $out .= ' VARBINARY(' . $data['length'] . ')'; + } else { + $out .= ' BINARY(' . $data['length'] . ')'; + } break; } } diff --git a/app/vendor/cakephp/cakephp/src/Database/Schema/PostgresSchema.php b/app/vendor/cakephp/cakephp/src/Database/Schema/PostgresSchema.php index 0a5a2742a..0d8ee340f 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Schema/PostgresSchema.php +++ b/app/vendor/cakephp/cakephp/src/Database/Schema/PostgresSchema.php @@ -359,7 +359,6 @@ public function columnSql(TableSchema $schema, $name) TableSchema::TYPE_TINYINTEGER => ' SMALLINT', TableSchema::TYPE_SMALLINTEGER => ' SMALLINT', TableSchema::TYPE_BINARY_UUID => ' UUID', - TableSchema::TYPE_BINARY => ' BYTEA', TableSchema::TYPE_BOOLEAN => ' BOOLEAN', TableSchema::TYPE_FLOAT => ' FLOAT', TableSchema::TYPE_DECIMAL => ' DECIMAL', @@ -387,6 +386,9 @@ public function columnSql(TableSchema $schema, $name) if ($data['type'] === TableSchema::TYPE_TEXT && $data['length'] !== TableSchema::LENGTH_TINY) { $out .= ' TEXT'; } + if ($data['type'] === TableSchema::TYPE_BINARY) { + $out .= ' BYTEA'; + } if ($data['type'] === TableSchema::TYPE_STRING || ($data['type'] === TableSchema::TYPE_TEXT && $data['length'] === TableSchema::LENGTH_TINY) @@ -397,7 +399,7 @@ public function columnSql(TableSchema $schema, $name) $type = ' CHAR'; } $out .= $type; - if (isset($data['length']) && $data['length'] != 36) { + if (isset($data['length'])) { $out .= '(' . (int)$data['length'] . ')'; } } diff --git a/app/vendor/cakephp/cakephp/src/Database/Schema/SqliteSchema.php b/app/vendor/cakephp/cakephp/src/Database/Schema/SqliteSchema.php index 67da6d427..3384df6f4 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Schema/SqliteSchema.php +++ b/app/vendor/cakephp/cakephp/src/Database/Schema/SqliteSchema.php @@ -102,7 +102,7 @@ protected function _convertColumn($column) if ($col === 'binary' && $length === 16) { return ['type' => TableSchema::TYPE_BINARY_UUID, 'length' => null]; } - if (in_array($col, ['blob', 'clob', 'binary'])) { + if (in_array($col, ['blob', 'clob', 'binary', 'varbinary'])) { return ['type' => TableSchema::TYPE_BINARY, 'length' => $length]; } if (in_array($col, ['date', 'time', 'timestamp', 'datetime'])) { @@ -294,7 +294,6 @@ public function columnSql(TableSchema $schema, $name) TableSchema::TYPE_INTEGER => ' INTEGER', TableSchema::TYPE_BIGINTEGER => ' BIGINT', TableSchema::TYPE_BOOLEAN => ' BOOLEAN', - TableSchema::TYPE_BINARY => ' BLOB', TableSchema::TYPE_FLOAT => ' FLOAT', TableSchema::TYPE_DECIMAL => ' DECIMAL', TableSchema::TYPE_DATE => ' DATE', @@ -340,6 +339,14 @@ public function columnSql(TableSchema $schema, $name) } } + if ($data['type'] === TableSchema::TYPE_BINARY) { + if (isset($data['length'])) { + $out .= ' BLOB(' . (int)$data['length'] . ')'; + } else { + $out .= ' BLOB'; + } + } + $integerTypes = [ TableSchema::TYPE_TINYINTEGER, TableSchema::TYPE_SMALLINTEGER, @@ -507,8 +514,9 @@ public function truncateTableSql(TableSchema $schema) */ public function hasSequences() { - $result = $this->_driver - ->prepare('SELECT 1 FROM sqlite_master WHERE name = "sqlite_sequence"'); + $result = $this->_driver->prepare( + 'SELECT 1 FROM sqlite_master WHERE name = "sqlite_sequence"' + ); $result->execute(); $this->_hasSequences = (bool)$result->rowCount(); $result->closeCursor(); diff --git a/app/vendor/cakephp/cakephp/src/Database/Schema/SqlserverSchema.php b/app/vendor/cakephp/cakephp/src/Database/Schema/SqlserverSchema.php index e44e574b0..74bfc91e8 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Schema/SqlserverSchema.php +++ b/app/vendor/cakephp/cakephp/src/Database/Schema/SqlserverSchema.php @@ -138,8 +138,8 @@ protected function _convertColumn($col, $length = null, $precision = null, $scal return ['type' => TableSchema::TYPE_TEXT, 'length' => null]; } - if ($col === 'image' || strpos($col, 'binary')) { - return ['type' => TableSchema::TYPE_BINARY, 'length' => null]; + if ($col === 'image' || strpos($col, 'binary') !== false) { + return ['type' => TableSchema::TYPE_BINARY, 'length' => $length]; } if ($col === 'uniqueidentifier') { @@ -371,12 +371,17 @@ public function columnSql(TableSchema $schema, $name) } if ($data['type'] === TableSchema::TYPE_BINARY) { - $out .= ' VARBINARY'; + if (!isset($data['length']) + || in_array($data['length'], [TableSchema::LENGTH_MEDIUM, TableSchema::LENGTH_LONG], true)) { + $data['length'] = 'MAX'; + } - if ($data['length'] !== TableSchema::LENGTH_TINY) { - $out .= '(MAX)'; + if ($data['length'] === 1) { + $out .= ' BINARY(1)'; } else { - $out .= sprintf('(%s)', TableSchema::LENGTH_TINY); + $out .= ' VARBINARY'; + + $out .= sprintf('(%s)', $data['length']); } } diff --git a/app/vendor/cakephp/cakephp/src/Database/Schema/TableSchema.php b/app/vendor/cakephp/cakephp/src/Database/Schema/TableSchema.php index 8bd4a28aa..b2cfdae0a 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Schema/TableSchema.php +++ b/app/vendor/cakephp/cakephp/src/Database/Schema/TableSchema.php @@ -780,7 +780,7 @@ public function temporary($temporary = null) { deprecationWarning( 'TableSchema::temporary() is deprecated. ' . - 'Use TableSchema::setTemporary()/getTemporary() instead.' + 'Use TableSchema::setTemporary()/isTemporary() instead.' ); if ($temporary !== null) { return $this->setTemporary($temporary); diff --git a/app/vendor/cakephp/cakephp/src/Database/Schema/TableSchemaInterface.php b/app/vendor/cakephp/cakephp/src/Database/Schema/TableSchemaInterface.php index 04afc9ccf..8bb2c701b 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Schema/TableSchemaInterface.php +++ b/app/vendor/cakephp/cakephp/src/Database/Schema/TableSchemaInterface.php @@ -18,8 +18,6 @@ /** * An interface used by database TableSchema objects. - * - * Deprecated 3.5.0: Use Cake\Database\TableSchemaAwareInterface instead. */ interface TableSchemaInterface extends SchemaInterface { @@ -201,7 +199,7 @@ public function getIndex($name); /** * Get the names of all the indexes in the table. * - * @return array + * @return string[] */ public function indexes(); @@ -246,7 +244,7 @@ public function dropConstraint($name); /** * Get the names of all the constraints in the table. * - * @return array + * @return string[] */ public function constraints(); } diff --git a/app/vendor/cakephp/cakephp/src/Database/SchemaCache.php b/app/vendor/cakephp/cakephp/src/Database/SchemaCache.php index c70d2432b..5f6433fbf 100644 --- a/app/vendor/cakephp/cakephp/src/Database/SchemaCache.php +++ b/app/vendor/cakephp/cakephp/src/Database/SchemaCache.php @@ -93,6 +93,7 @@ public function clear($name = null) * * @param \Cake\Database\Connection $connection Connection object * @return \Cake\Database\Schema\Collection|\Cake\Database\Schema\CachedCollection + * @throws \RuntimeException If given connection object is not compatible with schema caching */ public function getSchema(Connection $connection) { diff --git a/app/vendor/cakephp/cakephp/src/Database/SqlDialectTrait.php b/app/vendor/cakephp/cakephp/src/Database/SqlDialectTrait.php index 51bac658b..182818c02 100644 --- a/app/vendor/cakephp/cakephp/src/Database/SqlDialectTrait.php +++ b/app/vendor/cakephp/cakephp/src/Database/SqlDialectTrait.php @@ -38,19 +38,19 @@ public function quoteIdentifier($identifier) } // string - if (preg_match('/^[\w-]+$/', $identifier)) { + if (preg_match('/^[\w-]+$/u', $identifier)) { return $this->_startQuote . $identifier . $this->_endQuote; } // string.string - if (preg_match('/^[\w-]+\.[^ \*]*$/', $identifier)) { + if (preg_match('/^[\w-]+\.[^ \*]*$/u', $identifier)) { $items = explode('.', $identifier); return $this->_startQuote . implode($this->_endQuote . '.' . $this->_startQuote, $items) . $this->_endQuote; } // string.* - if (preg_match('/^[\w-]+\.\*$/', $identifier)) { + if (preg_match('/^[\w-]+\.\*$/u', $identifier)) { return $this->_startQuote . str_replace('.*', $this->_endQuote . '.*', $identifier); } @@ -60,19 +60,19 @@ public function quoteIdentifier($identifier) } // Alias.field AS thing - if (preg_match('/^([\w-]+(\.[\w-\s]+|\(.*\))*)\s+AS\s*([\w-]+)$/i', $identifier, $matches)) { + if (preg_match('/^([\w-]+(\.[\w\s-]+|\(.*\))*)\s+AS\s*([\w-]+)$/ui', $identifier, $matches)) { return $this->quoteIdentifier($matches[1]) . ' AS ' . $this->quoteIdentifier($matches[3]); } // string.string with spaces - if (preg_match('/^([\w-]+\.[\w][\w\s\-]*[\w])(.*)/', $identifier, $matches)) { + if (preg_match('/^([\w-]+\.[\w][\w\s\-]*[\w])(.*)/u', $identifier, $matches)) { $items = explode('.', $matches[1]); $field = implode($this->_endQuote . '.' . $this->_startQuote, $items); return $this->_startQuote . $field . $this->_endQuote . $matches[2]; } - if (preg_match('/^[\w-_\s]*[\w-_]+/', $identifier)) { + if (preg_match('/^[\w_\s-]*[\w_-]+/u', $identifier)) { return $this->_startQuote . $identifier . $this->_endQuote; } diff --git a/app/vendor/cakephp/cakephp/src/Database/Statement/BufferedStatement.php b/app/vendor/cakephp/cakephp/src/Database/Statement/BufferedStatement.php index 9fd6b5e38..17b819fe3 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Statement/BufferedStatement.php +++ b/app/vendor/cakephp/cakephp/src/Database/Statement/BufferedStatement.php @@ -14,54 +14,176 @@ */ namespace Cake\Database\Statement; +use Cake\Database\StatementInterface; +use Cake\Database\TypeConverterTrait; +use Iterator; + /** * A statement decorator that implements buffered results. * * This statement decorator will save fetched results in memory, allowing * the iterator to be rewound and reused. */ -class BufferedStatement extends StatementDecorator +class BufferedStatement implements Iterator, StatementInterface { + use TypeConverterTrait; /** - * Records count + * If true, all rows were fetched * - * @var int + * @var bool + */ + protected $_allFetched = false; + + /** + * The decorated statement + * + * @var \Cake\Database\StatementInterface */ - protected $_count = 0; + protected $statement; /** - * Array of results + * The driver for the statement + * + * @var \Cake\Database\DriverInterface + */ + protected $_driver; + + /** + * The in-memory cache containing results from previous iterators * * @var array */ - protected $_records = []; + protected $buffer = []; /** - * If true, all rows were fetched + * Whether or not this statement has already been executed * * @var bool */ - protected $_allFetched = false; + protected $_hasExecuted = false; /** - * Current record pointer + * The current iterator index. * * @var int */ - protected $_counter = 0; + protected $index = 0; /** - * Execute the statement and return the results. + * Constructor * - * @param array|null $params list of values to be bound to query - * @return bool true on success, false otherwise + * @param \Cake\Database\StatementInterface $statement Statement implementation such as PDOStatement + * @param \Cake\Database\Driver $driver Driver instance + */ + public function __construct(StatementInterface $statement, $driver) + { + $this->statement = $statement; + $this->_driver = $driver; + } + + /** + * Magic getter to return $queryString as read-only. + * + * @param string $property internal property to get + * @return mixed + */ + public function __get($property) + { + if ($property === 'queryString') { + return $this->statement->queryString; + } + } + + /** + * {@inheritDoc} + */ + public function bindValue($column, $value, $type = 'string') + { + $this->statement->bindValue($column, $value, $type); + } + + /** + * {@inheritDoc} + */ + public function closeCursor() + { + $this->statement->closeCursor(); + } + + /** + * {@inheritDoc} + */ + public function columnCount() + { + return $this->statement->columnCount(); + } + + /** + * {@inheritDoc} + */ + public function errorCode() + { + return $this->statement->errorCode(); + } + + /** + * {@inheritDoc} + */ + public function errorInfo() + { + return $this->statement->errorInfo(); + } + + /** + * {@inheritDoc} */ public function execute($params = null) { $this->_reset(); + $this->_hasExecuted = true; + + return $this->statement->execute($params); + } + + /** + * {@inheritDoc} + */ + public function fetchColumn($position) + { + $result = $this->fetch(static::FETCH_TYPE_NUM); + if (isset($result[$position])) { + return $result[$position]; + } - return parent::execute($params); + return false; + } + + /** + * Statements can be passed as argument for count() to return the number + * for affected rows from last execution. + * + * @return int + */ + public function count() + { + return $this->rowCount(); + } + + /** + * {@inheritDoc} + */ + public function bind($params, $types) + { + $this->statement->bind($params, $types); + } + + /** + * {@inheritDoc} + */ + public function lastInsertId($table = null, $column = null) + { + return $this->statement->lastInsertId($table, $column); } /** @@ -70,28 +192,32 @@ public function execute($params = null) * @param string $type The type to fetch. * @return array|false */ - public function fetch($type = parent::FETCH_TYPE_NUM) + public function fetch($type = self::FETCH_TYPE_NUM) { if ($this->_allFetched) { - $row = ($this->_counter < $this->_count) ? $this->_records[$this->_counter++] : false; - $row = ($row && $type === static::FETCH_TYPE_NUM) ? array_values($row) : $row; + $row = false; + if (isset($this->buffer[$this->index])) { + $row = $this->buffer[$this->index]; + } + $this->index += 1; + + if ($row && $type === static::FETCH_TYPE_NUM) { + return array_values($row); + } return $row; } - $record = parent::fetch($type); - + $record = $this->statement->fetch($type); if ($record === false) { $this->_allFetched = true; - $this->_counter = $this->_count + 1; - $this->_statement->closeCursor(); + $this->statement->closeCursor(); return false; } + $this->buffer[] = $record; - $this->_count++; - - return $this->_records[] = $record; + return $record; } /** @@ -99,7 +225,9 @@ public function fetch($type = parent::FETCH_TYPE_NUM) */ public function fetchAssoc() { - return $this->fetch(static::FETCH_TYPE_ASSOC); + $result = $this->fetch(static::FETCH_TYPE_ASSOC); + + return $result ?: []; } /** @@ -108,18 +236,19 @@ public function fetchAssoc() * @param string $type The type to fetch. * @return array */ - public function fetchAll($type = parent::FETCH_TYPE_NUM) + public function fetchAll($type = self::FETCH_TYPE_NUM) { if ($this->_allFetched) { - return $this->_records; + return $this->buffer; + } + $results = $this->statement->fetchAll($type); + if ($results !== false) { + $this->buffer = array_merge($this->buffer, $results); } - - $this->_records = parent::fetchAll($type); - $this->_count = count($this->_records); $this->_allFetched = true; - $this->_statement->closeCursor(); + $this->statement->closeCursor(); - return $this->_records; + return $this->buffer; } /** @@ -128,34 +257,88 @@ public function fetchAll($type = parent::FETCH_TYPE_NUM) public function rowCount() { if (!$this->_allFetched) { - $counter = $this->_counter; - while ($this->fetch(static::FETCH_TYPE_ASSOC)) { - } - $this->_counter = $counter; + $this->fetchAll(static::FETCH_TYPE_ASSOC); } - return $this->_count; + return count($this->buffer); + } + + /** + * Reset all properties + * + * @return void + */ + protected function _reset() + { + $this->buffer = []; + $this->_allFetched = false; + $this->index = 0; + } + + /** + * Returns the current key in the iterator + * + * @return mixed + */ + public function key() + { + return $this->index; + } + + /** + * Returns the current record in the iterator + * + * @return mixed + */ + public function current() + { + return $this->buffer[$this->index]; } /** - * Rewind the _counter property + * Rewinds the collection * * @return void */ public function rewind() { - $this->_counter = 0; + $this->index = 0; } /** - * Reset all properties + * Returns whether or not the iterator has more elements + * + * @return bool + */ + public function valid() + { + $old = $this->index; + $row = $this->fetch(self::FETCH_TYPE_ASSOC); + + // Restore the index as fetch() increments during + // the cache scenario. + $this->index = $old; + + return $row !== false; + } + + /** + * Advances the iterator pointer to the next element * * @return void */ - protected function _reset() + public function next() { - $this->_count = $this->_counter = 0; - $this->_records = []; - $this->_allFetched = false; + $this->index += 1; + } + + /** + * Get the wrapped statement + * + * @return \Cake\Database\StatementInterface + */ + public function getInnerStatement() + { + return $this->statement; } } diff --git a/app/vendor/cakephp/cakephp/src/Database/Statement/StatementDecorator.php b/app/vendor/cakephp/cakephp/src/Database/Statement/StatementDecorator.php index bd9d29b0b..9ae35c481 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Statement/StatementDecorator.php +++ b/app/vendor/cakephp/cakephp/src/Database/Statement/StatementDecorator.php @@ -32,30 +32,8 @@ */ class StatementDecorator implements StatementInterface, Countable, IteratorAggregate { - use TypeConverterTrait; - /** - * Used to designate that numeric indexes be returned in a result when calling fetch methods - * - * @var string - */ - const FETCH_TYPE_NUM = 'num'; - - /** - * Used to designate that an associated array be returned in a result when calling fetch methods - * - * @var string - */ - const FETCH_TYPE_ASSOC = 'assoc'; - - /** - * Used to designate that a stdClass object be returned in a result when calling fetch methods - * - * @var string - */ - const FETCH_TYPE_OBJ = 'obj'; - /** * Statement instance implementation, such as PDOStatement * or any other custom implementation. @@ -221,18 +199,20 @@ public function fetch($type = self::FETCH_TYPE_NUM) * Returns the next row in a result set as an associative array. Calling this function is the same as calling * $statement->fetch(StatementDecorator::FETCH_TYPE_ASSOC). If no results are found false is returned. * - * @return array|false Result array containing columns and values an an associative array or false if no results + * @return array Result array containing columns and values an an associative array or an empty array if no results */ public function fetchAssoc() { - return $this->fetch(static::FETCH_TYPE_ASSOC); + $result = $this->fetch(static::FETCH_TYPE_ASSOC); + + return $result ?: []; } /** * Returns the value of the result at position. * * @param int $position The numeric position of the column to retrieve in the result - * @return mixed|false Returns the specific value of the column designated at $position + * @return mixed Returns the specific value of the column designated at $position */ public function fetchColumn($position) { diff --git a/app/vendor/cakephp/cakephp/src/Database/StatementInterface.php b/app/vendor/cakephp/cakephp/src/Database/StatementInterface.php index f88ef22de..eddb3a6c2 100644 --- a/app/vendor/cakephp/cakephp/src/Database/StatementInterface.php +++ b/app/vendor/cakephp/cakephp/src/Database/StatementInterface.php @@ -17,9 +17,31 @@ /** * Represents a database statement. Concrete implementations * can either use PDOStatement or a native driver + * + * @property-read string $queryString */ interface StatementInterface { + /** + * Used to designate that numeric indexes be returned in a result when calling fetch methods + * + * @var string + */ + const FETCH_TYPE_NUM = 'num'; + + /** + * Used to designate that an associated array be returned in a result when calling fetch methods + * + * @var string + */ + const FETCH_TYPE_ASSOC = 'assoc'; + + /** + * Used to designate that a stdClass object be returned in a result when calling fetch methods + * + * @var string + */ + const FETCH_TYPE_OBJ = 'obj'; /** * Assign a value to a positional or named variable in prepared query. If using diff --git a/app/vendor/cakephp/cakephp/src/Database/Type.php b/app/vendor/cakephp/cakephp/src/Database/Type.php index ad566e8c2..24df3f6f1 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Type.php +++ b/app/vendor/cakephp/cakephp/src/Database/Type.php @@ -138,6 +138,7 @@ public static function buildAll() public static function set($name, Type $instance) { static::$_builtTypes[$name] = $instance; + static::$_types[$name] = get_class($instance); } /** diff --git a/app/vendor/cakephp/cakephp/src/Database/Type/BatchCastingInterface.php b/app/vendor/cakephp/cakephp/src/Database/Type/BatchCastingInterface.php index 0424d9cbc..fe1d614a2 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Type/BatchCastingInterface.php +++ b/app/vendor/cakephp/cakephp/src/Database/Type/BatchCastingInterface.php @@ -28,7 +28,7 @@ interface BatchCastingInterface * this type. * * @param array $values The original array of values containing the fields to be casted - * @param array $fields The field keys to cast + * @param string[] $fields The field keys to cast * @param \Cake\Database\Driver $driver Object from which database preferences and configuration will be extracted. * @return array */ diff --git a/app/vendor/cakephp/cakephp/src/Database/Type/BinaryType.php b/app/vendor/cakephp/cakephp/src/Database/Type/BinaryType.php index 5426c6864..bd27ede8a 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Type/BinaryType.php +++ b/app/vendor/cakephp/cakephp/src/Database/Type/BinaryType.php @@ -107,7 +107,7 @@ public function toStatement($value, Driver $driver) } /** - * Marshalls flat data into PHP objects. + * Marshals flat data into PHP objects. * * Most useful for converting request data into PHP objects * that make sense for the rest of the ORM/Database layers. diff --git a/app/vendor/cakephp/cakephp/src/Database/Type/BinaryUuidType.php b/app/vendor/cakephp/cakephp/src/Database/Type/BinaryUuidType.php index 47b88aa7f..8eba18fc5 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Type/BinaryUuidType.php +++ b/app/vendor/cakephp/cakephp/src/Database/Type/BinaryUuidType.php @@ -116,7 +116,7 @@ public function toStatement($value, Driver $driver) } /** - * Marshalls flat data into PHP objects. + * Marshals flat data into PHP objects. * * Most useful for converting request data into PHP objects * that make sense for the rest of the ORM/Database layers. @@ -155,9 +155,9 @@ protected function convertBinaryUuidToString($binary) * Converts a string uuid to a binary representation * * - * @param mixed $string The value to convert. + * @param string $string The value to convert. * - * @return mixed Converted value. + * @return string Converted value. */ protected function convertStringToBinaryUuid($string) { diff --git a/app/vendor/cakephp/cakephp/src/Database/Type/BoolType.php b/app/vendor/cakephp/cakephp/src/Database/Type/BoolType.php index 8c95c5f66..65e529eb9 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Type/BoolType.php +++ b/app/vendor/cakephp/cakephp/src/Database/Type/BoolType.php @@ -145,7 +145,7 @@ public function toStatement($value, Driver $driver) } /** - * Marshalls request data into PHP booleans. + * Marshals request data into PHP booleans. * * @param mixed $value The value to convert. * @return bool|null Converted value. @@ -161,6 +161,9 @@ public function marshal($value) if ($value === 'false') { return false; } + if (!is_scalar($value)) { + return null; + } return !empty($value); } diff --git a/app/vendor/cakephp/cakephp/src/Database/Type/DecimalType.php b/app/vendor/cakephp/cakephp/src/Database/Type/DecimalType.php index c4e48d1b9..64acf40a4 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Type/DecimalType.php +++ b/app/vendor/cakephp/cakephp/src/Database/Type/DecimalType.php @@ -70,7 +70,7 @@ public function __construct($name = null) /** * Convert integer data into the database format. * - * @param string|int|float $value The value to convert. + * @param mixed $value The value to convert. * @param \Cake\Database\Driver $driver The driver instance to convert with. * @return string|null * @throws \InvalidArgumentException @@ -96,10 +96,9 @@ public function toDatabase($value, Driver $driver) /** * Convert float values to PHP floats * - * @param null|string|resource $value The value to convert. + * @param mixed $value The value to convert. * @param \Cake\Database\Driver $driver The driver instance to convert with. * @return float|null - * @throws \Cake\Core\Exception\Exception */ public function toPHP($value, Driver $driver) { @@ -113,7 +112,7 @@ public function toPHP($value, Driver $driver) /** * {@inheritDoc} * - * @return array + * @return float[] */ public function manyToPHP(array $values, array $fields, Driver $driver) { @@ -141,10 +140,10 @@ public function toStatement($value, Driver $driver) } /** - * Marshalls request data into PHP floats. + * Marshals request data into PHP floats. * * @param mixed $value The value to convert. - * @return mixed Converted value. + * @return float|string|null Converted value. */ public function marshal($value) { @@ -157,11 +156,11 @@ public function marshal($value) if (is_numeric($value)) { return (float)$value; } - if (is_array($value)) { - return 1; + if (is_string($value) && preg_match('/^[0-9,. ]+$/', $value)) { + return $value; } - return $value; + return null; } /** @@ -170,6 +169,7 @@ public function marshal($value) * * @param bool $enable Whether or not to enable * @return $this + * @throws \RuntimeException */ public function useLocaleParser($enable = true) { diff --git a/app/vendor/cakephp/cakephp/src/Database/Type/FloatType.php b/app/vendor/cakephp/cakephp/src/Database/Type/FloatType.php index 06089e2d2..bc3a70dd5 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Type/FloatType.php +++ b/app/vendor/cakephp/cakephp/src/Database/Type/FloatType.php @@ -102,7 +102,7 @@ public function toPHP($value, Driver $driver) /** * {@inheritDoc} * - * @return array + * @return float[] */ public function manyToPHP(array $values, array $fields, Driver $driver) { @@ -130,7 +130,7 @@ public function toStatement($value, Driver $driver) } /** - * Marshalls request data into PHP floats. + * Marshals request data into PHP floats. * * @param mixed $value The value to convert. * @return float|null Converted value. @@ -140,17 +140,17 @@ public function marshal($value) if ($value === null || $value === '') { return null; } - if (is_numeric($value)) { - return (float)$value; - } if (is_string($value) && $this->_useLocaleParser) { return $this->_parseValue($value); } - if (is_array($value)) { - return 1.0; + if (is_numeric($value)) { + return (float)$value; + } + if (is_string($value) && preg_match('/^[0-9,. ]+$/', $value)) { + return $value; } - return $value; + return null; } /** diff --git a/app/vendor/cakephp/cakephp/src/Database/Type/IntegerType.php b/app/vendor/cakephp/cakephp/src/Database/Type/IntegerType.php index b8903ea5b..106e6380e 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Type/IntegerType.php +++ b/app/vendor/cakephp/cakephp/src/Database/Type/IntegerType.php @@ -51,6 +51,23 @@ public function __construct($name = null) $this->_name = $name; } + /** + * Checks if the value is not a numeric value + * + * @throws \InvalidArgumentException + * @param mixed $value Value to check + * @return void + */ + protected function checkNumeric($value) + { + if (!is_numeric($value)) { + throw new InvalidArgumentException(sprintf( + 'Cannot convert value of type `%s` to integer', + getTypeName($value) + )); + } + } + /** * Convert integer data into the database format. * @@ -64,12 +81,7 @@ public function toDatabase($value, Driver $driver) return null; } - if (!is_scalar($value)) { - throw new InvalidArgumentException(sprintf( - 'Cannot convert value of type `%s` to integer', - getTypeName($value) - )); - } + $this->checkNumeric($value); return (int)$value; } @@ -93,7 +105,7 @@ public function toPHP($value, Driver $driver) /** * {@inheritDoc} * - * @return array + * @return int[] */ public function manyToPHP(array $values, array $fields, Driver $driver) { @@ -101,6 +113,9 @@ public function manyToPHP(array $values, array $fields, Driver $driver) if (!isset($values[$field])) { continue; } + + $this->checkNumeric($values[$field]); + $values[$field] = (int)$values[$field]; } @@ -120,7 +135,7 @@ public function toStatement($value, Driver $driver) } /** - * Marshalls request data into PHP floats. + * Marshals request data into PHP floats. * * @param mixed $value The value to convert. * @return int|null Converted value. @@ -130,12 +145,9 @@ public function marshal($value) if ($value === null || $value === '') { return null; } - if (is_numeric($value) || ctype_digit($value)) { + if (is_numeric($value)) { return (int)$value; } - if (is_array($value)) { - return 1; - } return null; } diff --git a/app/vendor/cakephp/cakephp/src/Database/Type/JsonType.php b/app/vendor/cakephp/cakephp/src/Database/Type/JsonType.php index 2c48dbb16..f16c7fd75 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Type/JsonType.php +++ b/app/vendor/cakephp/cakephp/src/Database/Type/JsonType.php @@ -110,7 +110,7 @@ public function toStatement($value, Driver $driver) } /** - * Marshalls request data into a JSON compatible structure. + * Marshals request data into a JSON compatible structure. * * @param mixed $value The value to convert. * @return mixed Converted value. diff --git a/app/vendor/cakephp/cakephp/src/Database/Type/StringType.php b/app/vendor/cakephp/cakephp/src/Database/Type/StringType.php index d5b015033..e75b3d574 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Type/StringType.php +++ b/app/vendor/cakephp/cakephp/src/Database/Type/StringType.php @@ -84,7 +84,7 @@ public function toStatement($value, Driver $driver) } /** - * Marshalls request data into PHP strings. + * Marshals request data into PHP strings. * * @param mixed $value The value to convert. * @return string|null Converted value. diff --git a/app/vendor/cakephp/cakephp/src/Database/TypeInterface.php b/app/vendor/cakephp/cakephp/src/Database/TypeInterface.php index 82e17f477..d30153162 100644 --- a/app/vendor/cakephp/cakephp/src/Database/TypeInterface.php +++ b/app/vendor/cakephp/cakephp/src/Database/TypeInterface.php @@ -49,7 +49,7 @@ public function toPHP($value, Driver $driver); public function toStatement($value, Driver $driver); /** - * Marshalls flat data into PHP objects. + * Marshals flat data into PHP objects. * * Most useful for converting request data into PHP objects, * that make sense for the rest of the ORM/Database layers. diff --git a/app/vendor/cakephp/cakephp/src/Database/TypeMap.php b/app/vendor/cakephp/cakephp/src/Database/TypeMap.php index e3005d611..8eea99fb7 100644 --- a/app/vendor/cakephp/cakephp/src/Database/TypeMap.php +++ b/app/vendor/cakephp/cakephp/src/Database/TypeMap.php @@ -26,7 +26,7 @@ class TypeMap * Used to avoid repetition when calling multiple functions inside this class that * may require a custom type for a specific field. * - * @var array + * @var string[] */ protected $_defaults; @@ -36,14 +36,14 @@ class TypeMap * Used to avoid repetition when calling multiple functions inside this class that * may require a custom type for a specific field. * - * @var array + * @var string[] */ protected $_types = []; /** * Creates an instance with the given defaults * - * @param array $defaults The defaults to use. + * @param string[] $defaults The defaults to use. */ public function __construct(array $defaults = []) { @@ -51,10 +51,13 @@ public function __construct(array $defaults = []) } /** - * Configures a map of default fields and their associated types to be - * used as the default list of types for every function in this class - * with a $types param. Useful to avoid repetition when calling the same - * functions using the same fields and types. + * Configures a map of fields and associated type. + * + * These values will be used as the default mapping of types for every function + * in this instance that supports a `$types` param. + * + * This method is useful when you want to avoid repeating type definitions + * as setting types overwrites the last set of types. * * ### Example * @@ -62,9 +65,10 @@ public function __construct(array $defaults = []) * $query->setDefaults(['created' => 'datetime', 'is_visible' => 'boolean']); * ``` * - * This method will replace all the existing type maps with the ones provided. + * This method will replace all the existing default mappings with the ones provided. + * To add into the mappings use `addDefaults()`. * - * @param array $defaults Associative array where keys are field names and values + * @param string[] $defaults Associative array where keys are field names and values * are the correspondent type. * @return $this */ @@ -78,7 +82,7 @@ public function setDefaults(array $defaults) /** * Returns the currently configured types. * - * @return array + * @return string[] */ public function getDefaults() { @@ -99,7 +103,8 @@ public function getDefaults() * $query->defaults(['created' => 'datetime', 'is_visible' => 'boolean']); * ``` * - * This method will replace all the existing type maps with the ones provided. + * This method will replace all the existing default mappings with the ones provided. + * To add into the mappings use addDefaults() * * @deprecated 3.4.0 Use setDefaults()/getDefaults() instead. * @param array|null $defaults associative array where keys are field names and values @@ -124,7 +129,7 @@ public function defaults(array $defaults = null) * * If a key already exists it will not be overwritten. * - * @param array $types The additional types to add. + * @param string[] $types The additional types to add. * @return void */ public function addDefaults(array $types) @@ -143,7 +148,7 @@ public function addDefaults(array $types) * * This method will replace all the existing type maps with the ones provided. * - * @param array $types Associative array where keys are field names and values + * @param string[] $types Associative array where keys are field names and values * are the correspondent type. * @return $this */ @@ -157,7 +162,7 @@ public function setTypes(array $types) /** * Gets a map of fields and their associated types for single-use. * - * @return array + * @return string[] */ public function getTypes() { @@ -201,7 +206,7 @@ public function types(array $types = null) * null will be returned. * * @param string $column The type for a given column - * @return null|string + * @return string|null */ public function type($column) { @@ -218,7 +223,7 @@ public function type($column) /** * Returns an array of all types mapped types * - * @return array + * @return string[] */ public function toArray() { diff --git a/app/vendor/cakephp/cakephp/src/Database/TypeMapTrait.php b/app/vendor/cakephp/cakephp/src/Database/TypeMapTrait.php index cc2a28f9e..2ed42e12c 100644 --- a/app/vendor/cakephp/cakephp/src/Database/TypeMapTrait.php +++ b/app/vendor/cakephp/cakephp/src/Database/TypeMapTrait.php @@ -77,10 +77,18 @@ public function typeMap($typeMap = null) } /** - * Allows setting default types when chaining query. + * Overwrite the default type mappings for fields + * in the implementing object. + * + * This method is useful if you need to set type mappings that are shared across + * multiple functions/expressions in a query. + * + * To add a default without overwriting existing ones + * use `getTypeMap()->addDefaults()` * * @param array $types The array of types to set. * @return $this + * @see \Cake\Database\TypeMap::setDefaults() */ public function setDefaultTypes(array $types) { diff --git a/app/vendor/cakephp/cakephp/src/Database/composer.json b/app/vendor/cakephp/cakephp/src/Database/composer.json index 3de651356..c19711558 100644 --- a/app/vendor/cakephp/cakephp/src/Database/composer.json +++ b/app/vendor/cakephp/cakephp/src/Database/composer.json @@ -27,10 +27,8 @@ "php": ">=5.6.0", "cakephp/cache": "^3.6.0", "cakephp/core": "^3.6.0", - "cakephp/datasource": "^3.6.0" - }, - "suggest": { - "cakephp/log": "Require this if you want to use the built-in query logger" + "cakephp/datasource": "^3.6.0", + "cakephp/log": "^3.6.0" }, "autoload": { "psr-4": { diff --git a/app/vendor/cakephp/cakephp/src/Datasource/ConnectionInterface.php b/app/vendor/cakephp/cakephp/src/Datasource/ConnectionInterface.php index fbc3b2417..c67af789e 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/ConnectionInterface.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/ConnectionInterface.php @@ -25,6 +25,10 @@ * @method \Cake\Database\Query newQuery() * @method \Cake\Database\StatementInterface prepare($sql) * @method \Cake\Database\StatementInterface execute($query, $params = [], array $types = []) + * @method $this enableQueryLogging($value) + * @method $this disableQueryLogging() + * @method $this disableSavePoints() + * @method bool isQueryLoggingEnabled() * @method string quote($value, $type = null) */ interface ConnectionInterface diff --git a/app/vendor/cakephp/cakephp/src/Datasource/EntityInterface.php b/app/vendor/cakephp/cakephp/src/Datasource/EntityInterface.php index d10832a8a..cb0e2052e 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/EntityInterface.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/EntityInterface.php @@ -27,8 +27,9 @@ * @method array getHidden() * @method $this setVirtual(array $properties, $merge = false) * @method array getVirtual() - * @method $this setDirty($property, $isDirty) + * @method $this setDirty($property, $isDirty = true) * @method bool isDirty($property = null) + * @method bool hasErrors($includeNested = true) * @method array getErrors() * @method array getError($field) * @method array setErrors(array $fields, $overwrite = false) @@ -39,6 +40,7 @@ * @method string getSource() * @method array extractOriginal(array $properties) * @method array extractOriginalChanged(array $properties) + * @method array getVisible() * * @property mixed $id Alias for commonly used primary key. */ diff --git a/app/vendor/cakephp/cakephp/src/Datasource/EntityTrait.php b/app/vendor/cakephp/cakephp/src/Datasource/EntityTrait.php index d66f53de9..991f699cc 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/EntityTrait.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/EntityTrait.php @@ -15,6 +15,7 @@ namespace Cake\Datasource; use Cake\Collection\Collection; +use Cake\Utility\Hash; use Cake\Utility\Inflector; use InvalidArgumentException; use Traversable; @@ -555,7 +556,7 @@ public function getVirtual() } /** - * Get the list of visible properties. + * Gets the list of visible properties. * * The list of visible properties is all standard properties * plus virtual properties minus hidden properties. @@ -563,7 +564,7 @@ public function getVirtual() * @return array A list of properties that are 'visible' in all * representations. */ - public function visibleProperties() + public function getVisible() { $properties = array_keys($this->_properties); $properties = array_merge($properties, $this->_virtual); @@ -571,6 +572,26 @@ public function visibleProperties() return array_diff($properties, $this->_hidden); } + /** + * Gets the list of visible properties. + * + * The list of visible properties is all standard properties + * plus virtual properties minus hidden properties. + * + * @return array A list of properties that are 'visible' in all + * representations. + * @deprecated 3.8.0 Use getVisible() instead. + */ + public function visibleProperties() + { + deprecationWarning( + get_called_class() . '::visibleProperties() is deprecated. ' . + 'Use getVisible() instead.' + ); + + return $this->getVisible(); + } + /** * Returns an array with all the properties that have been set * to this entity @@ -583,7 +604,7 @@ public function visibleProperties() public function toArray() { $result = []; - foreach ($this->visibleProperties() as $property) { + foreach ($this->getVisible() as $property) { $value = $this->get($property); if (is_array($value)) { $result[$property] = []; @@ -611,7 +632,7 @@ public function toArray() */ public function jsonSerialize() { - return $this->extract($this->visibleProperties()); + return $this->extract($this->getVisible()); } /** @@ -840,7 +861,7 @@ public function isDirty($property = null) /** * Gets the dirty properties. * - * @return array + * @return string[] */ public function getDirty() { @@ -891,6 +912,31 @@ public function isNew($new = null) return $this->_new = $new; } + /** + * Returns whether this entity has errors. + * + * @param bool $includeNested true will check nested entities for hasErrors() + * @return bool + */ + public function hasErrors($includeNested = true) + { + if (Hash::filter($this->_errors)) { + return true; + } + + if ($includeNested === false) { + return false; + } + + foreach ($this->_properties as $property) { + if ($this->_readHasErrors($property)) { + return true; + } + } + + return false; + } + /** * Returns all validation errors. * @@ -937,21 +983,21 @@ public function getError($field) * $entity->setErrors(['salary' => ['message'], 'name' => ['another message']]); * ``` * - * @param array $fields The array of errors to set. + * @param array $errors The array of errors to set. * @param bool $overwrite Whether or not to overwrite pre-existing errors for $fields * @return $this */ - public function setErrors(array $fields, $overwrite = false) + public function setErrors(array $errors, $overwrite = false) { if ($overwrite) { - foreach ($fields as $f => $error) { + foreach ($errors as $f => $error) { $this->_errors[$f] = (array)$error; } return $this; } - foreach ($fields as $f => $error) { + foreach ($errors as $f => $error) { $this->_errors += [$f => []]; // String messages are appended to the list, @@ -1007,13 +1053,13 @@ public function setError($field, $errors, $overwrite = false) * $entity->errors('salary', ['must be numeric', 'must be a positive number']); * * // Returns the error messages for a single field - * $entity->errors('salary'); + * $entity->getErrors('salary'); * * // Returns all error messages indexed by field name - * $entity->errors(); + * $entity->getErrors(); * * // Sets the error messages for multiple fields at once - * $entity->errors(['salary' => ['message'], 'name' => ['another message']); + * $entity->getErrors(['salary' => ['message'], 'name' => ['another message']); * ``` * * When used as a setter, this method will return this entity instance for method @@ -1054,13 +1100,19 @@ public function errors($field = null, $errors = null, $overwrite = false) */ protected function _nestedErrors($field) { - $path = explode('.', $field); - // Only one path element, check for nested entity with error. - if (count($path) === 1) { - return $this->_readError($this->get($path[0])); + if (strpos($field, '.') === false) { + return $this->_readError($this->get($field)); + } + // Try reading the errors data with field as a simple path + $error = Hash::get($this->_errors, $field); + if ($error !== null) { + return $error; } + $path = explode('.', $field); + // Traverse down the related entities/arrays for + // the relevant entity. $entity = $this; $len = count($path); while ($len) { @@ -1090,6 +1142,29 @@ protected function _nestedErrors($field) return []; } + /** + * Reads if there are errors for one or many objects. + * + * @param mixed $object The object to read errors from. + * @return bool + */ + protected function _readHasErrors($object) + { + if ($object instanceof EntityInterface && $object->hasErrors()) { + return true; + } + + if (is_array($object)) { + foreach ($object as $value) { + if ($this->_readHasErrors($value)) { + return true; + } + } + } + + return false; + } + /** * Read the error(s) from one or many objects. * @@ -1132,7 +1207,7 @@ public function getInvalid() * Get a single value of an invalid field. Returns null if not set. * * @param string $field The name of the field. - * @return mixed + * @return mixed|null */ public function getInvalidField($field) { @@ -1410,6 +1485,7 @@ public function __debugInfo() '[dirty]' => $this->_dirty, '[original]' => $this->_original, '[virtual]' => $this->_virtual, + '[hasErrors]' => $this->hasErrors(), '[errors]' => $this->_errors, '[invalid]' => $this->_invalid, '[repository]' => $this->_registryAlias diff --git a/app/vendor/cakephp/cakephp/src/Datasource/ModelAwareTrait.php b/app/vendor/cakephp/cakephp/src/Datasource/ModelAwareTrait.php index c9b982c7e..f9d4c8220 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/ModelAwareTrait.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/ModelAwareTrait.php @@ -35,7 +35,10 @@ trait ModelAwareTrait * Plugin classes should use `Plugin.Comments` style names to correctly load * models from the correct plugin. * - * @var string + * Use false to not use auto-loading on this object. Null auto-detects based on + * controller name. + * + * @var string|false|null */ public $modelClass; @@ -63,7 +66,7 @@ trait ModelAwareTrait */ protected function _setModelClass($name) { - if (empty($this->modelClass)) { + if ($this->modelClass === null) { $this->modelClass = $name; } } @@ -77,7 +80,8 @@ protected function _setModelClass($name) * If a repository provider does not return an object a MissingModelException will * be thrown. * - * @param string|null $modelClass Name of model class to load. Defaults to $this->modelClass + * @param string|null $modelClass Name of model class to load. Defaults to $this->modelClass. + * The name can be an alias like `'Post'` or FQCN like `App\Model\Table\PostsTable::class`. * @param string|null $modelType The type of repository to load. Defaults to the modelType() value. * @return \Cake\Datasource\RepositoryInterface The model instance created. * @throws \Cake\Datasource\Exception\MissingModelException If the model class cannot be found. @@ -97,7 +101,19 @@ public function loadModel($modelClass = null, $modelType = null) } } - list(, $alias) = pluginSplit($modelClass, true); + $alias = null; + $options = []; + if (strpos($modelClass, '\\') === false) { + list(, $alias) = pluginSplit($modelClass, true); + } else { + $options['className'] = $modelClass; + $alias = substr( + $modelClass, + strrpos($modelClass, '\\') + 1, + -strlen($modelType) + ); + $modelClass = $alias; + } if (isset($this->{$alias})) { return $this->{$alias}; @@ -109,7 +125,7 @@ public function loadModel($modelClass = null, $modelType = null) if (!isset($factory)) { $factory = FactoryLocator::get($modelType); } - $this->{$alias} = $factory($modelClass); + $this->{$alias} = $factory($modelClass, $options); if (!$this->{$alias}) { throw new MissingModelException([$modelClass, $modelType]); } diff --git a/app/vendor/cakephp/cakephp/src/Datasource/Paginator.php b/app/vendor/cakephp/cakephp/src/Datasource/Paginator.php index 9d8ef5a96..aecae8057 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/Paginator.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/Paginator.php @@ -221,7 +221,7 @@ public function paginate($object, array $params = [], array $settings = []) 'nextPage' => $count > ($page * $limit), 'pageCount' => $pageCount, 'sort' => $options['sort'], - 'direction' => current($order), + 'direction' => isset($options['sort']) ? current($order) : null, 'limit' => $defaults['limit'] != $limit ? $limit : null, 'sortDefault' => $sortDefault, 'directionDefault' => $directionDefault, @@ -363,7 +363,12 @@ public function validateSort(RepositoryInterface $object, array $options) if (!in_array($direction, ['asc', 'desc'])) { $direction = 'asc'; } + $order = (isset($options['order']) && is_array($options['order'])) ? $options['order'] : []; + if ($order && $options['sort'] && strpos($options['sort'], '.') === false) { + $order = $this->_removeAliases($order, $object->getAlias()); + } + $options['order'] = [$options['sort'] => $direction] + $order; } else { $options['sort'] = null; @@ -401,6 +406,35 @@ public function validateSort(RepositoryInterface $object, array $options) return $options; } + /** + * Remove alias if needed. + * + * @param array $fields Current fields + * @param string $model Current model alias + * @return array $fields Unaliased fields where applicable + */ + protected function _removeAliases($fields, $model) + { + $result = []; + foreach ($fields as $field => $sort) { + if (strpos($field, '.') === false) { + $result[$field] = $sort; + continue; + } + + list ($alias, $currentField) = explode('.', $field); + + if ($alias === $model) { + $result[$currentField] = $sort; + continue; + } + + $result[$field] = $sort; + } + + return $result; + } + /** * Prefixes the field with the table alias if possible. * diff --git a/app/vendor/cakephp/cakephp/src/Datasource/QueryTrait.php b/app/vendor/cakephp/cakephp/src/Datasource/QueryTrait.php index 8932fd6af..abb4fdd43 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/QueryTrait.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/QueryTrait.php @@ -61,7 +61,7 @@ trait QueryTrait /** * A query cacher instance if this query has caching enabled. * - * @var \Cake\Datasource\QueryCacher + * @var \Cake\Datasource\QueryCacher|null */ protected $_cache; diff --git a/app/vendor/cakephp/cakephp/src/Datasource/README.md b/app/vendor/cakephp/cakephp/src/Datasource/README.md index 9eae4c035..093a6c26f 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/README.md +++ b/app/vendor/cakephp/cakephp/src/Datasource/README.md @@ -44,13 +44,13 @@ easy: ```php use Cake\Datasource\ConnectionManager; -ConnectionManager::config('master', [ +ConnectionManager::config('connection-one', [ 'className' => 'MyApp\Connections\CustomConnection', 'param1' => 'value', 'param2' => 'another value' ]); -ConnectionManager::config('slave', [ +ConnectionManager::config('connection-two', [ 'className' => 'MyApp\Connections\CustomConnection', 'param1' => 'different value', 'param2' => 'another value' diff --git a/app/vendor/cakephp/cakephp/src/Datasource/RepositoryInterface.php b/app/vendor/cakephp/cakephp/src/Datasource/RepositoryInterface.php index 8d09c0dd0..171835df1 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/RepositoryInterface.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/RepositoryInterface.php @@ -18,8 +18,10 @@ * Describes the methods that any class representing a data storage should * comply with. * - * @method $this setAlias($alias) + * @method $this setAlias(string $alias) * @method string getAlias() + * @method $this setRegistryAlias(string $alias) + * @method string getRegistryAlias() */ interface RepositoryInterface { diff --git a/app/vendor/cakephp/cakephp/src/Datasource/SchemaInterface.php b/app/vendor/cakephp/cakephp/src/Datasource/SchemaInterface.php index 997ffa1e4..69e5e3999 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/SchemaInterface.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/SchemaInterface.php @@ -52,7 +52,7 @@ public function name(); * - `comment` The comment for the column. * * @param string $name The name of the column - * @param array $attrs The attributes for the column. + * @param array|string $attrs The attributes for the column. * @return $this */ public function addColumn($name, $attrs); @@ -86,7 +86,7 @@ public function removeColumn($name); /** * Get the column names in the table. * - * @return array + * @return string[] */ public function columns(); diff --git a/app/vendor/cakephp/cakephp/src/Error/BaseErrorHandler.php b/app/vendor/cakephp/cakephp/src/Error/BaseErrorHandler.php index b8a51a0de..89e70959e 100644 --- a/app/vendor/cakephp/cakephp/src/Error/BaseErrorHandler.php +++ b/app/vendor/cakephp/cakephp/src/Error/BaseErrorHandler.php @@ -354,13 +354,34 @@ protected function _requestContext($request) * @return string Formatted message */ protected function _getMessage(Exception $exception) + { + $message = $this->getMessageForException($exception); + + $request = Router::getRequest(); + if ($request) { + $message .= $this->_requestContext($request); + } + + return $message; + } + + /** + * Generate the message for the exception + * + * @param \Exception $exception The exception to log a message for. + * @param bool $isPrevious False for original exception, true for previous + * @return string Error message + */ + protected function getMessageForException($exception, $isPrevious = false) { $exception = $exception instanceof PHP7ErrorException ? $exception->getError() : $exception; $config = $this->_options; + $message = sprintf( - '[%s] %s in %s on line %s', + '%s[%s] %s in %s on line %s', + $isPrevious ? "\nCaused by: " : '', get_class($exception), $exception->getMessage(), $exception->getFile(), @@ -375,15 +396,15 @@ protected function _getMessage(Exception $exception) } } - $request = Router::getRequest(); - if ($request) { - $message .= $this->_requestContext($request); - } - if (!empty($config['trace'])) { $message .= "\nStack Trace:\n" . $exception->getTraceAsString() . "\n\n"; } + $previous = $exception->getPrevious(); + if ($previous) { + $message .= $this->getMessageForException($previous, true); + } + return $message; } diff --git a/app/vendor/cakephp/cakephp/src/Error/Debugger.php b/app/vendor/cakephp/cakephp/src/Error/Debugger.php index b7b88bf47..353cbef0c 100644 --- a/app/vendor/cakephp/cakephp/src/Error/Debugger.php +++ b/app/vendor/cakephp/cakephp/src/Error/Debugger.php @@ -249,7 +249,10 @@ public static function dump($var, $depth = 3) */ public static function log($var, $level = 'debug', $depth = 3) { - $source = static::trace(['start' => 1]) . "\n"; + /** @var string $source */ + $source = static::trace(['start' => 1]); + $source .= "\n"; + Log::write($level, "\n" . $source . static::exportVar($var, $depth)); } @@ -266,7 +269,7 @@ public static function log($var, $level = 'debug', $depth = 3) * - `start` - The stack frame to start generating a trace from. Defaults to 0 * * @param array $options Format for outputting stack trace. - * @return mixed Formatted stack trace. + * @return string|array Formatted stack trace. * @link https://book.cakephp.org/3.0/en/development/debugging.html#generating-stack-traces */ public static function trace(array $options = []) @@ -288,7 +291,7 @@ public static function trace(array $options = []) * * @param array|\Exception $backtrace Trace as array or an exception object. * @param array $options Format for outputting stack trace. - * @return mixed Formatted stack trace. + * @return string|array Formatted stack trace. * @link https://book.cakephp.org/3.0/en/development/debugging.html#generating-stack-traces */ public static function formatTrace($backtrace, $options = []) diff --git a/app/vendor/cakephp/cakephp/src/Error/ExceptionRenderer.php b/app/vendor/cakephp/cakephp/src/Error/ExceptionRenderer.php index 1d45150e3..1b4194640 100644 --- a/app/vendor/cakephp/cakephp/src/Error/ExceptionRenderer.php +++ b/app/vendor/cakephp/cakephp/src/Error/ExceptionRenderer.php @@ -22,6 +22,7 @@ use Cake\Event\Event; use Cake\Http\Exception\HttpException; use Cake\Http\Response; +use Cake\Http\ServerRequest; use Cake\Http\ServerRequestFactory; use Cake\Routing\DispatcherFactory; use Cake\Routing\Router; @@ -55,28 +56,36 @@ class ExceptionRenderer implements ExceptionRendererInterface * * @var \Exception */ - public $error; + protected $error; /** * Controller instance. * * @var \Cake\Controller\Controller */ - public $controller; + protected $controller; /** * Template to render for Cake\Core\Exception\Exception * * @var string */ - public $template = ''; + protected $template = ''; /** * The method corresponding to the Exception this object is for. * * @var string */ - public $method = ''; + protected $method = ''; + + /** + * If set, this will be request used to create the controller that will render + * the error. + * + * @var \Cake\Http\ServerRequest|null + */ + protected $request = null; /** * Creates the controller to perform rendering on the error response. @@ -84,10 +93,12 @@ class ExceptionRenderer implements ExceptionRendererInterface * code error depending on the code used to construct the error. * * @param \Exception $exception Exception. + * @param \Cake\Http\ServerRequest $request The request - if this is set it will be used instead of creating a new one */ - public function __construct(Exception $exception) + public function __construct(Exception $exception, ServerRequest $request = null) { $this->error = $exception; + $this->request = $request; $this->controller = $this->_getController(); } @@ -114,14 +125,43 @@ protected function _unwrap($exception) */ protected function _getController() { - if (!$request = Router::getRequest(true)) { - $request = ServerRequestFactory::fromGlobals(); + $request = $this->request; + $routerRequest = Router::getRequest(true); + // Fallback to the request in the router or make a new one from + // $_SERVER + if ($request === null) { + $request = $routerRequest ?: ServerRequestFactory::fromGlobals(); + } + + // If the current request doesn't have routing data, but we + // found a request in the router context copy the params over + if ($request->getParam('controller') === false && $routerRequest !== null) { + $request = $request->withAttribute('params', $routerRequest->getAttribute('params')); } + $response = new Response(); $controller = null; try { - $class = App::className('Error', 'Controller', 'Controller'); + $namespace = 'Controller'; + $prefix = $request->getParam('prefix'); + if ($prefix) { + if (strpos($prefix, '/') === false) { + $namespace .= '/' . Inflector::camelize($prefix); + } else { + $prefixes = array_map( + 'Cake\Utility\Inflector::camelize', + explode('/', $prefix) + ); + $namespace .= '/' . implode('/', $prefixes); + } + } + + $class = App::className('Error', $namespace, 'Controller'); + if (!$class && $namespace !== 'Controller') { + $class = App::className('Error', 'Controller', 'Controller'); + } + /* @var \Cake\Controller\Controller $controller */ $controller = new $class($request, $response); $controller->startupProcess(); @@ -160,10 +200,7 @@ public function render() $template = $this->_template($exception, $method, $code); $unwrapped = $this->_unwrap($exception); - $isDebug = Configure::read('debug'); - if (($isDebug || $exception instanceof HttpException) && - method_exists($this, $method) - ) { + if (method_exists($this, $method)) { return $this->_customMethod($method, $unwrapped); } @@ -185,6 +222,8 @@ public function render() 'code' => $code, '_serialize' => ['message', 'url', 'code'] ]; + + $isDebug = Configure::read('debug'); if ($isDebug) { $viewVars['trace'] = Debugger::formatTrace($unwrapped->getTrace(), [ 'format' => 'array', @@ -300,17 +339,18 @@ protected function _template(Exception $exception, $method, $code) } /** - * Get an error code value within range 400 to 506 + * Get HTTP status code. * * @param \Exception $exception Exception. - * @return int Error code value within range 400 to 506 + * @return int A valid HTTP error status code. */ protected function _code(Exception $exception) { $code = 500; + $exception = $this->_unwrap($exception); $errorCode = $exception->getCode(); - if ($errorCode >= 400 && $errorCode < 506) { + if ($errorCode >= 400 && $errorCode < 600) { $code = $errorCode; } @@ -395,4 +435,70 @@ protected function _shutdown() return $result->getData('response'); } + + /** + * Magic accessor for properties made protected. + * + * @param string $name Name of the attribute to get. + * @return mixed + */ + public function __get($name) + { + $protected = [ + 'error', + 'controller', + 'template', + 'method', + ]; + if (in_array($name, $protected, true)) { + deprecationWarning(sprintf( + 'ExceptionRenderer::$%s is now protected and should no longer be accessed in public context.', + $name + )); + } + + return $this->{$name}; + } + + /** + * Magic setter for properties made protected. + * + * @param string $name Name to property. + * @param mixed $value Value for property. + * @return void + */ + public function __set($name, $value) + { + $protected = [ + 'error', + 'controller', + 'template', + 'method', + ]; + if (in_array($name, $protected, true)) { + deprecationWarning(sprintf( + 'ExceptionRenderer::$%s is now protected and should no longer be accessed in public context.', + $name + )); + } + + $this->{$name} = $value; + } + + /** + * Returns an array that can be used to describe the internal state of this + * object. + * + * @return array + */ + public function __debugInfo() + { + return [ + 'error' => $this->error, + 'request' => $this->request, + 'controller' => $this->controller, + 'template' => $this->template, + 'method' => $this->method, + ]; + } } diff --git a/app/vendor/cakephp/cakephp/src/Error/Middleware/ErrorHandlerMiddleware.php b/app/vendor/cakephp/cakephp/src/Error/Middleware/ErrorHandlerMiddleware.php index e433a6fb3..e99342c5d 100644 --- a/app/vendor/cakephp/cakephp/src/Error/Middleware/ErrorHandlerMiddleware.php +++ b/app/vendor/cakephp/cakephp/src/Error/Middleware/ErrorHandlerMiddleware.php @@ -30,8 +30,6 @@ * * Traps exceptions and converts them into HTML or content-type appropriate * error pages using the CakePHP ExceptionRenderer. - * - * @mixin \Cake\Core\InstanceConfigTrait */ class ErrorHandlerMiddleware { @@ -113,7 +111,7 @@ public function __invoke($request, $response, $next) */ public function handleException($exception, $request, $response) { - $renderer = $this->getRenderer($exception); + $renderer = $this->getRenderer($exception, $request); try { $res = $renderer->render(); $this->logException($request, $exception); @@ -148,10 +146,11 @@ protected function handleInternalError($response) * Get a renderer instance * * @param \Exception $exception The exception being rendered. + * @param \Psr\Http\Message\ServerRequestInterface $request The request. * @return \Cake\Error\ExceptionRendererInterface The exception renderer. * @throws \Exception When the renderer class cannot be found. */ - protected function getRenderer($exception) + protected function getRenderer($exception, $request) { if (!$this->exceptionRenderer) { $this->exceptionRenderer = $this->getConfig('exceptionRenderer') ?: ExceptionRenderer::class; @@ -171,11 +170,11 @@ protected function getRenderer($exception) )); } - return new $class($exception); + return new $class($exception, $request); } $factory = $this->exceptionRenderer; - return $factory($exception); + return $factory($exception, $request); } /** @@ -208,9 +207,31 @@ protected function logException($request, $exception) * @return string Error message */ protected function getMessage($request, $exception) + { + $message = $this->getMessageForException($exception); + + $message .= "\nRequest URL: " . $request->getRequestTarget(); + $referer = $request->getHeaderLine('Referer'); + if ($referer) { + $message .= "\nReferer URL: " . $referer; + } + $message .= "\n\n"; + + return $message; + } + + /** + * Generate the message for the exception + * + * @param \Exception $exception The exception to log a message for. + * @param bool $isPrevious False for original exception, true for previous + * @return string Error message + */ + protected function getMessageForException($exception, $isPrevious = false) { $message = sprintf( - '[%s] %s', + '%s[%s] %s', + $isPrevious ? "\nCaused by: " : '', get_class($exception), $exception->getMessage() ); @@ -222,13 +243,14 @@ protected function getMessage($request, $exception) $message .= "\nException Attributes: " . var_export($exception->getAttributes(), true); } } - $message .= "\nRequest URL: " . $request->getRequestTarget(); - $referer = $request->getHeaderLine('Referer'); - if ($referer) { - $message .= "\nReferer URL: " . $referer; - } + if ($this->getConfig('trace')) { - $message .= "\nStack Trace:\n" . $exception->getTraceAsString() . "\n\n"; + $message .= "\n" . $exception->getTraceAsString(); + } + + $previous = $exception->getPrevious(); + if ($previous) { + $message .= $this->getMessageForException($previous, true); } return $message; diff --git a/app/vendor/cakephp/cakephp/src/Event/EventDispatcherTrait.php b/app/vendor/cakephp/cakephp/src/Event/EventDispatcherTrait.php index bc7ee7b02..08c403c06 100644 --- a/app/vendor/cakephp/cakephp/src/Event/EventDispatcherTrait.php +++ b/app/vendor/cakephp/cakephp/src/Event/EventDispatcherTrait.php @@ -24,7 +24,7 @@ trait EventDispatcherTrait * Instance of the Cake\Event\EventManager this object is using * to dispatch inner events. * - * @var \Cake\Event\EventManager + * @var \Cake\Event\EventManagerInterface|\Cake\Event\EventManager */ protected $_eventManager; diff --git a/app/vendor/cakephp/cakephp/src/Event/EventListenerInterface.php b/app/vendor/cakephp/cakephp/src/Event/EventListenerInterface.php index 5f7af679a..b117237b6 100644 --- a/app/vendor/cakephp/cakephp/src/Event/EventListenerInterface.php +++ b/app/vendor/cakephp/cakephp/src/Event/EventListenerInterface.php @@ -38,7 +38,7 @@ interface EventListenerInterface * } * ``` * - * @return array associative array or event key names pointing to the function + * @return array Associative array or event key names pointing to the function * that should be called in the object when the respective event is fired */ public function implementedEvents(); diff --git a/app/vendor/cakephp/cakephp/src/Filesystem/File.php b/app/vendor/cakephp/cakephp/src/Filesystem/File.php index e0ed1a44c..b39dc999b 100644 --- a/app/vendor/cakephp/cakephp/src/Filesystem/File.php +++ b/app/vendor/cakephp/cakephp/src/Filesystem/File.php @@ -574,7 +574,7 @@ public function folder() /** * Copy the File to $dest * - * @param string $dest Destination for the copy + * @param string $dest Absolute path to copy the file to. * @param bool $overwrite Overwrite $dest if exists * @return bool Success */ diff --git a/app/vendor/cakephp/cakephp/src/Filesystem/Folder.php b/app/vendor/cakephp/cakephp/src/Filesystem/Folder.php index a148782ae..b425cbfe2 100644 --- a/app/vendor/cakephp/cakephp/src/Filesystem/Folder.php +++ b/app/vendor/cakephp/cakephp/src/Filesystem/Folder.php @@ -354,12 +354,30 @@ public static function isRegisteredStreamWrapper($path) * * @param string $path Path to check * @return string Set of slashes ("\\" or "/") + * + * @deprecated 3.7.0 This method will be removed in 4.0.0. Use correctSlashFor() instead. */ public static function normalizePath($path) { + deprecationWarning('Folder::normalizePath() is deprecated. Use Folder::correctSlashFor() instead.'); + return Folder::correctSlashFor($path); } + /** + * Returns a correct set of slashes for given $path. (\\ for Windows paths and / for other paths.) + * + * @param string $path Path to transform + * @return string Path with the correct set of slashes ("\\" or "/") + */ + public static function normalizeFullPath($path) + { + $to = Folder::correctSlashFor($path); + $from = ($to == '/' ? '\\' : '/'); + + return str_replace($from, $to, $path); + } + /** * Returns a correct set of slashes for given $path. (\\ for Windows paths and / for other paths.) * @@ -571,13 +589,18 @@ public function tree($path = null, $exceptions = false, $type = null) return []; } + /** + * @var string $itemPath + * @var \RecursiveDirectoryIterator $fsIterator + */ foreach ($iterator as $itemPath => $fsIterator) { if ($skipHidden) { $subPathName = $fsIterator->getSubPathname(); - if ($subPathName{0} === '.' || strpos($subPathName, DIRECTORY_SEPARATOR . '.') !== false) { + if ($subPathName[0] === '.' || strpos($subPathName, DIRECTORY_SEPARATOR . '.') !== false) { continue; } } + /** @var \FilesystemIterator $item */ $item = $fsIterator->current(); if (!empty($exceptions) && isset($exceptions[$item->getFilename()])) { continue; diff --git a/app/vendor/cakephp/cakephp/src/Form/Form.php b/app/vendor/cakephp/cakephp/src/Form/Form.php index 5c2197b34..019950e53 100644 --- a/app/vendor/cakephp/cakephp/src/Form/Form.php +++ b/app/vendor/cakephp/cakephp/src/Form/Form.php @@ -20,6 +20,7 @@ use Cake\Event\EventListenerInterface; use Cake\Event\EventManager; use Cake\Form\Schema; +use Cake\Utility\Hash; use Cake\Validation\Validator; use Cake\Validation\ValidatorAwareInterface; use Cake\Validation\ValidatorAwareTrait; @@ -89,6 +90,14 @@ class Form implements EventListenerInterface, EventDispatcherInterface, Validato */ protected $_validator; + /** + * Form's data. + * + * @var array + * @since 3.7.0 + */ + protected $_data = []; + /** * Constructor * @@ -242,8 +251,27 @@ public function validate(array $data) * to `validate()` or `execute()`. * * @return array Last set validation errors. + * @deprecated 3.7.0 Use Form::getErrors() instead. */ public function errors() + { + deprecationWarning( + 'Form::errors() is deprecated. ' . + 'Use Form::getErrors() instead.' + ); + + return $this->getErrors(); + } + + /** + * Get the errors in the form + * + * Will return the errors from the last call + * to `validate()` or `execute()`. + * + * @return array Last set validation errors. + */ + public function getErrors() { return $this->_errors; } @@ -304,6 +332,37 @@ protected function _execute(array $data) return true; } + /** + * Get field data. + * + * @param string|null $field The field name or null to get data array with + * all fields. + * @return mixed + * @since 3.7.0 + */ + public function getData($field = null) + { + if ($field === null) { + return $this->_data; + } + + return Hash::get($this->_data, $field); + } + + /** + * Set form data. + * + * @param array $data Data array. + * @return $this + * @since 3.7.0 + */ + public function setData(array $data) + { + $this->_data = $data; + + return $this; + } + /** * Get the printable version of a Form instance. * @@ -313,7 +372,7 @@ public function __debugInfo() { $special = [ '_schema' => $this->schema()->__debugInfo(), - '_errors' => $this->errors(), + '_errors' => $this->getErrors(), '_validator' => $this->getValidator()->__debugInfo() ]; diff --git a/app/vendor/cakephp/cakephp/src/Form/README.md b/app/vendor/cakephp/cakephp/src/Form/README.md index a5b5af56e..62d2b1d6f 100644 --- a/app/vendor/cakephp/cakephp/src/Form/README.md +++ b/app/vendor/cakephp/cakephp/src/Form/README.md @@ -55,7 +55,7 @@ You can always define additional public methods as you need as well. ```php $contact = new ContactForm(); $success = $contact->execute($data); -$errors = $contact->errors(); +$errors = $contact->getErrors(); ``` ## Documentation diff --git a/app/vendor/cakephp/cakephp/src/Form/Schema.php b/app/vendor/cakephp/cakephp/src/Form/Schema.php index 08d11fcdb..11d927f44 100644 --- a/app/vendor/cakephp/cakephp/src/Form/Schema.php +++ b/app/vendor/cakephp/cakephp/src/Form/Schema.php @@ -89,7 +89,7 @@ public function removeField($name) /** * Get the list of fields in the schema. * - * @return array The list of field names. + * @return string[] The list of field names. */ public function fields() { diff --git a/app/vendor/cakephp/cakephp/src/Http/Client.php b/app/vendor/cakephp/cakephp/src/Http/Client.php index 94dd2704a..ecc194dac 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Client.php +++ b/app/vendor/cakephp/cakephp/src/Http/Client.php @@ -16,6 +16,9 @@ use Cake\Core\App; use Cake\Core\Exception\Exception; use Cake\Core\InstanceConfigTrait; +use Cake\Http\Client\AdapterInterface; +use Cake\Http\Client\Adapter\Curl; +use Cake\Http\Client\Adapter\Stream; use Cake\Http\Client\Request; use Cake\Http\Cookie\CookieCollection; use Cake\Http\Cookie\CookieInterface; @@ -90,12 +93,9 @@ * a proxy if you need to use one. The type sub option can be used to * specify which authentication strategy you want to use. * CakePHP comes with built-in support for basic authentication. - * - * @mixin \Cake\Core\InstanceConfigTrait */ class Client { - use InstanceConfigTrait; /** @@ -104,7 +104,7 @@ class Client * @var array */ protected $_defaultConfig = [ - 'adapter' => 'Cake\Http\Client\Adapter\Stream', + 'adapter' => null, 'host' => null, 'port' => null, 'scheme' => 'http', @@ -127,10 +127,9 @@ class Client protected $_cookies; /** - * Adapter for sending requests. Defaults to - * Cake\Http\Client\Adapter\Stream + * Adapter for sending requests. * - * @var \Cake\Http\Client\Adapter\Stream + * @var \Cake\Http\Client\AdapterInterface */ protected $_adapter; @@ -154,18 +153,35 @@ class Client * - ssl_verify_host - Verify that the certificate and hostname match. * Defaults to true. * - redirect - Number of redirects to follow. Defaults to false. + * - adapter - The adapter class name or instance. Defaults to + * \Cake\Http\Client\Adapter\Curl if `curl` extension is loaded else + * \Cake\Http\Client\Adapter\Stream. * * @param array $config Config options for scoped clients. + * @throws \InvalidArgumentException */ public function __construct($config = []) { $this->setConfig($config); $adapter = $this->_config['adapter']; - $this->setConfig('adapter', null); + if ($adapter === null) { + $adapter = Curl::class; + + if (!extension_loaded('curl')) { + $adapter = Stream::class; + } + } else { + $this->setConfig('adapter', null); + } + if (is_string($adapter)) { $adapter = new $adapter(); } + + if (!$adapter instanceof AdapterInterface) { + throw new InvalidArgumentException('Adapter must be an instance of Cake\Http\Client\AdapterInterface'); + } $this->_adapter = $adapter; if (!empty($this->_config['cookieJar'])) { @@ -179,7 +195,7 @@ public function __construct($config = []) /** * Get the cookies stored in the Client. * - * @return \Cake\Http\Client\CookieCollection + * @return \Cake\Http\Cookie\CookieCollection */ public function cookies() { @@ -191,6 +207,7 @@ public function cookies() * * @param \Cake\Http\Cookie\CookieInterface $cookie Cookie object. * @return $this + * @throws \InvalidArgumentException */ public function addCookie(CookieInterface $cookie) { @@ -591,7 +608,7 @@ protected function _addProxy(Request $request, $options) * * @param array $auth The authentication options to use. * @param array $options The overall request options to use. - * @return mixed Authentication strategy instance. + * @return object Authentication strategy instance. * @throws \Cake\Core\Exception\Exception when an invalid strategy is chosen. */ protected function _createAuth($auth, $options) diff --git a/app/vendor/cakephp/cakephp/src/Http/Client/Adapter/Curl.php b/app/vendor/cakephp/cakephp/src/Http/Client/Adapter/Curl.php new file mode 100644 index 000000000..3f1ace12f --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Http/Client/Adapter/Curl.php @@ -0,0 +1,188 @@ +buildOptions($request, $options); + curl_setopt_array($ch, $options); + + $body = $this->exec($ch); + if ($body === false) { + $errorCode = curl_errno($ch); + $error = curl_error($ch); + curl_close($ch); + + $status = 500; + if ($errorCode === CURLE_OPERATION_TIMEOUTED) { + $status = 504; + } + throw new HttpException("cURL Error ({$errorCode}) {$error}", $status); + } + + $responses = $this->createResponse($ch, $body); + curl_close($ch); + + return $responses; + } + + /** + * Convert client options into curl options. + * + * @param \Cake\Http\Client\Request $request The request. + * @param array $options The client options + * @return array + */ + public function buildOptions(Request $request, array $options) + { + $headers = []; + foreach ($request->getHeaders() as $key => $values) { + $headers[] = $key . ': ' . implode(', ', $values); + } + + $out = [ + CURLOPT_URL => (string)$request->getUri(), + CURLOPT_HTTP_VERSION => $this->getProtocolVersion($request), + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HEADER => true, + CURLOPT_HTTPHEADER => $headers + ]; + switch ($request->getMethod()) { + case Request::METHOD_GET: + $out[CURLOPT_HTTPGET] = true; + break; + + case Request::METHOD_POST: + $out[CURLOPT_POST] = true; + break; + + default: + $out[CURLOPT_POST] = true; + $out[CURLOPT_CUSTOMREQUEST] = $request->getMethod(); + break; + } + + $body = $request->getBody(); + if ($body) { + $body->rewind(); + $out[CURLOPT_POSTFIELDS] = $body->getContents(); + } + + if (empty($options['ssl_cafile'])) { + $options['ssl_cafile'] = CORE_PATH . 'config' . DIRECTORY_SEPARATOR . 'cacert.pem'; + } + if (!empty($options['ssl_verify_host'])) { + // Value of 1 or true is deprecated. Only 2 or 0 should be used now. + $options['ssl_verify_host'] = 2; + } + $optionMap = [ + 'timeout' => CURLOPT_TIMEOUT, + 'ssl_verify_peer' => CURLOPT_SSL_VERIFYPEER, + 'ssl_verify_host' => CURLOPT_SSL_VERIFYHOST, + 'ssl_cafile' => CURLOPT_CAINFO, + 'ssl_local_cert' => CURLOPT_SSLCERT, + 'ssl_passphrase' => CURLOPT_SSLCERTPASSWD, + ]; + foreach ($optionMap as $option => $curlOpt) { + if (isset($options[$option])) { + $out[$curlOpt] = $options[$option]; + } + } + if (isset($options['proxy']['proxy'])) { + $out[CURLOPT_PROXY] = $options['proxy']['proxy']; + } + if (isset($options['proxy']['username'])) { + $password = !empty($options['proxy']['password']) ? $options['proxy']['password'] : ''; + $out[CURLOPT_PROXYUSERPWD] = $options['proxy']['username'] . ':' . $password; + } + if (isset($options['curl']) && is_array($options['curl'])) { + // Can't use array_merge() because keys will be re-ordered. + foreach ($options['curl'] as $key => $value) { + $out[$key] = $value; + } + } + + return $out; + } + + /** + * Convert HTTP version number into curl value. + * + * @param \Cake\Http\Client\Request $request The request to get a protocol version for. + * @return int + */ + protected function getProtocolVersion(Request $request) + { + switch ($request->getProtocolVersion()) { + case '1.0': + return CURL_HTTP_VERSION_1_0; + case '1.1': + return CURL_HTTP_VERSION_1_1; + case '2.0': + if (defined('CURL_HTTP_VERSION_2_0')) { + return CURL_HTTP_VERSION_2_0; + } + throw new HttpException('libcurl 7.33 needed for HTTP 2.0 support'); + } + + return CURL_HTTP_VERSION_NONE; + } + + /** + * Convert the raw curl response into an Http\Client\Response + * + * @param resource $handle Curl handle + * @param string $responseData string The response data from curl_exec + * @return \Cake\Http\Client\Response + */ + protected function createResponse($handle, $responseData) + { + $headerSize = curl_getinfo($handle, CURLINFO_HEADER_SIZE); + $headers = trim(substr($responseData, 0, $headerSize)); + $body = substr($responseData, $headerSize); + $response = new Response(explode("\r\n", $headers), $body); + + return [$response]; + } + + /** + * Execute the curl handle. + * + * @param resource $ch Curl Resource handle + * @return string + */ + protected function exec($ch) + { + return curl_exec($ch); + } +} diff --git a/app/vendor/cakephp/cakephp/src/Http/Client/Adapter/Stream.php b/app/vendor/cakephp/cakephp/src/Http/Client/Adapter/Stream.php index 06835083b..8b0e4e2c0 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Client/Adapter/Stream.php +++ b/app/vendor/cakephp/cakephp/src/Http/Client/Adapter/Stream.php @@ -14,6 +14,7 @@ namespace Cake\Http\Client\Adapter; use Cake\Core\Exception\Exception; +use Cake\Http\Client\AdapterInterface; use Cake\Http\Client\Request; use Cake\Http\Client\Response; use Cake\Http\Exception\HttpException; @@ -24,7 +25,7 @@ * * This approach and implementation is partly inspired by Aura.Http */ -class Stream +class Stream implements AdapterInterface { /** @@ -63,11 +64,7 @@ class Stream protected $_connectionErrors = []; /** - * Send a request and get a response back. - * - * @param \Cake\Http\Client\Request $request The request object to send. - * @param array $options Array of options for the stream. - * @return array Array of populated Response objects + * {@inheritDoc} */ public function send(Request $request, array $options) { diff --git a/app/vendor/cakephp/cakephp/src/Http/Client/AdapterInterface.php b/app/vendor/cakephp/cakephp/src/Http/Client/AdapterInterface.php new file mode 100644 index 000000000..e3c5c9480 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Http/Client/AdapterInterface.php @@ -0,0 +1,28 @@ +getHeaders(); * ``` * - * You can also get at the headers using object access. When getting - * headers with object access, you have to use case-sensitive header - * names: - * - * ``` - * $val = $response->headers['Content-Type']; - * ``` - * * ### Get the response body * * You can access the response body stream using: @@ -60,11 +52,10 @@ * $content = $response->getBody(); * ``` * - * You can also use object access to get the string version - * of the response body: + * You can get the body string using: * * ``` - * $content = $response->body; + * $content = $response->getStringBody(); * ``` * * If your response body is in XML or JSON you can use @@ -74,9 +65,9 @@ * * ``` * // Get as xml - * $content = $response->xml + * $content = $response->getXml() * // Get as json - * $content = $response->json + * $content = $response->getJson() * ``` * * If the response cannot be decoded, null will be returned. @@ -88,12 +79,6 @@ * ``` * $content = $response->getStatusCode(); * ``` - * - * You can also use object access: - * - * ``` - * $content = $response->code; - * ``` */ class Response extends Message implements ResponseInterface { @@ -148,6 +133,20 @@ class Response extends Message implements ResponseInterface 'headers' => '_getHeaders', ]; + /** + * Map of deprecated magic properties. + * + * @var array + * @internal + */ + protected $_deprecatedMagicProperties = [ + 'cookies' => 'getCookies()', + 'body' => 'getStringBody()', + 'json' => 'getJson()', + 'xml' => 'getXml()', + 'headers' => 'getHeaders()', + ]; + /** * Constructor * @@ -211,6 +210,9 @@ protected function _parseHeaders($headers) $this->reasonPhrase = trim($matches[3]); continue; } + if (strpos($value, ':') === false) { + continue; + } list($name, $value) = explode(':', $value, 2); $value = trim($value); $name = trim($name); @@ -554,9 +556,14 @@ public function version() * @param callable|null $parser The callback to use to decode * the response body. * @return mixed The response body. + * @deprecated 3.7.0 Use getStringBody()/getJson()/getXml() instead. */ public function body($parser = null) { + deprecationWarning( + 'Response::body() is deprecated. Use getStringBody()/getJson()/getXml() instead.' + ); + $stream = $this->stream; $stream->rewind(); if ($parser) { @@ -566,6 +573,26 @@ public function body($parser = null) return $stream->getContents(); } + /** + * Get the response body as string. + * + * @return string + */ + public function getStringBody() + { + return $this->_getBody(); + } + + /** + * Get the response body as JSON decoded data. + * + * @return array|null + */ + public function getJson() + { + return $this->_getJson(); + } + /** * Get the response body as JSON decoded data. * @@ -580,6 +607,16 @@ protected function _getJson() return $this->_json = json_decode($this->_getBody(), true); } + /** + * Get the response body as XML decoded data. + * + * @return null|\SimpleXMLElement + */ + public function getXml() + { + return $this->_getXml(); + } + /** * Get the response body as XML decoded data. * @@ -619,7 +656,7 @@ protected function _getHeaders() /** * Provides magic __get() support. * - * @return array + * @return string */ protected function _getBody() { @@ -641,9 +678,22 @@ public function __get($name) } $key = $this->_exposedProperties[$name]; if (substr($key, 0, 4) === '_get') { + deprecationWarning(sprintf( + 'Response::%s is deprecated. Use Response::%s instead.', + $name, + $this->_deprecatedMagicProperties[$name] + )); + return $this->{$key}(); } + if ($key === 'code') { + deprecationWarning( + 'Response::code() is deprecated. ' . + 'Use Response::getStatusCode() instead.' + ); + } + return $this->{$key}; } @@ -660,11 +710,24 @@ public function __isset($name) } $key = $this->_exposedProperties[$name]; if (substr($key, 0, 4) === '_get') { + deprecationWarning(sprintf( + 'Response::%s is deprecated. Use Response::%s instead.', + $name, + $this->_deprecatedMagicProperties[$name] + )); + $val = $this->{$key}(); return $val !== null; } + if ($key === 'code') { + deprecationWarning( + 'Response::code() is deprecated. ' . + 'Use Response::getStatusCode() instead.' + ); + } + return isset($this->{$key}); } } diff --git a/app/vendor/cakephp/cakephp/src/Http/ControllerFactory.php b/app/vendor/cakephp/cakephp/src/Http/ControllerFactory.php index b68e9ee10..02b5343a1 100644 --- a/app/vendor/cakephp/cakephp/src/Http/ControllerFactory.php +++ b/app/vendor/cakephp/cakephp/src/Http/ControllerFactory.php @@ -43,7 +43,10 @@ public function create(ServerRequest $request, Response $response) $this->missingController($request); } - return $reflection->newInstance($request, $response); + /** @var \Cake\Controller\Controller $controller */ + $controller = $reflection->newInstance($request, $response); + + return $controller; } /** diff --git a/app/vendor/cakephp/cakephp/src/Http/Cookie/Cookie.php b/app/vendor/cakephp/cakephp/src/Http/Cookie/Cookie.php index db524d16e..7d6f3bde6 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Cookie/Cookie.php +++ b/app/vendor/cakephp/cakephp/src/Http/Cookie/Cookie.php @@ -81,7 +81,7 @@ class Cookie implements CookieInterface * * @var string */ - protected $path = ''; + protected $path = '/'; /** * Domain @@ -124,7 +124,7 @@ public function __construct( $name, $value = '', $expiresAt = null, - $path = '', + $path = '/', $domain = '', $secure = false, $httpOnly = false diff --git a/app/vendor/cakephp/cakephp/src/Http/CorsBuilder.php b/app/vendor/cakephp/cakephp/src/Http/CorsBuilder.php index 02293e36d..cacbd4472 100644 --- a/app/vendor/cakephp/cakephp/src/Http/CorsBuilder.php +++ b/app/vendor/cakephp/cakephp/src/Http/CorsBuilder.php @@ -103,12 +103,12 @@ public function build() * Accepts a string or an array of domains that have CORS enabled. * You can use `*.example.com` wildcards to accept subdomains, or `*` to allow all domains * - * @param string|array $domain The allowed domains + * @param string|string[] $domains The allowed domains * @return $this */ - public function allowOrigin($domain) + public function allowOrigin($domains) { - $allowed = $this->_normalizeDomains((array)$domain); + $allowed = $this->_normalizeDomains((array)$domains); foreach ($allowed as $domain) { if (!preg_match($domain['preg'], $this->_origin)) { continue; @@ -124,7 +124,7 @@ public function allowOrigin($domain) /** * Normalize the origin to regular expressions and put in an array format * - * @param array $domains Domain names to normalize. + * @param string[] $domains Domain names to normalize. * @return array */ protected function _normalizeDomains($domains) @@ -150,7 +150,7 @@ protected function _normalizeDomains($domains) /** * Set the list of allowed HTTP Methods. * - * @param array $methods The allowed HTTP methods + * @param string[] $methods The allowed HTTP methods * @return $this */ public function allowMethods(array $methods) diff --git a/app/vendor/cakephp/cakephp/src/Http/Middleware/BodyParserMiddleware.php b/app/vendor/cakephp/cakephp/src/Http/Middleware/BodyParserMiddleware.php index bb77f2e7e..f26f67086 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Middleware/BodyParserMiddleware.php +++ b/app/vendor/cakephp/cakephp/src/Http/Middleware/BodyParserMiddleware.php @@ -40,7 +40,7 @@ class BodyParserMiddleware /** * The HTTP methods to parse data on. * - * @var array + * @var string[] */ protected $methods = ['PUT', 'POST', 'PATCH', 'DELETE']; @@ -79,7 +79,7 @@ public function __construct(array $options = []) /** * Set the HTTP methods to parse request bodies on. * - * @param array $methods The methods to parse data on. + * @param string[] $methods The methods to parse data on. * @return $this */ public function setMethods(array $methods) @@ -104,7 +104,7 @@ public function setMethods(array $methods) * }); * ``` * - * @param array $types An array of content-type header values to match. eg. application/json + * @param string[] $types An array of content-type header values to match. eg. application/json * @param callable $parser The parser function. Must return an array of data to be inserted * into the request. * @return $this @@ -131,7 +131,7 @@ public function addParser(array $types, callable $parser) */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next) { - if (!in_array($request->getMethod(), $this->methods)) { + if (!in_array($request->getMethod(), $this->methods, true)) { return $next($request, $response); } list($type) = explode(';', $request->getHeaderLine('Content-Type')); @@ -172,7 +172,7 @@ protected function decodeXml($body) try { $xml = Xml::build($body, ['return' => 'domdocument', 'readFile' => false]); // We might not get child nodes if there are nested inline entities. - if ($xml->childNodes->length > 0) { + if ((int)$xml->childNodes->length > 0) { return Xml::toArray($xml); } diff --git a/app/vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php b/app/vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php index 8e0b15590..4b11e35c6 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php +++ b/app/vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php @@ -14,6 +14,7 @@ */ namespace Cake\Http\Middleware; +use Cake\Http\Cookie\Cookie; use Cake\Http\Exception\InvalidCsrfTokenException; use Cake\Http\Response; use Cake\Http\ServerRequest; @@ -65,6 +66,15 @@ class CsrfProtectionMiddleware */ protected $_config = []; + /** + * Callback for deciding whether or not to skip the token check for particular request. + * + * CSRF protection token check will be skipped if the callback returns `true`. + * + * @var callable|null + */ + protected $whitelistCallback; + /** * Constructor * @@ -85,6 +95,12 @@ public function __construct(array $config = []) */ public function __invoke(ServerRequest $request, Response $response, $next) { + if ($this->whitelistCallback !== null + && call_user_func($this->whitelistCallback, $request) === true + ) { + return $next($request, $response); + } + $cookies = $request->getCookieParams(); $cookieData = Hash::get($cookies, $this->_config['cookieName']); @@ -107,6 +123,22 @@ public function __invoke(ServerRequest $request, Response $response, $next) return $next($request, $response); } + /** + * Set callback for allowing to skip token check for particular request. + * + * The callback will receive request instance as argument and must return + * `true` if you want to skip token check for the current request. + * + * @param callable $callback A callable. + * @return $this + */ + public function whitelistCallback(callable $callback) + { + $this->whitelistCallback = $callback; + + return $this; + } + /** * Checks if the request is POST, PUT, DELETE or PATCH and validates the CSRF token * @@ -115,7 +147,7 @@ public function __invoke(ServerRequest $request, Response $response, $next) */ protected function _validateAndUnsetTokenField(ServerRequest $request) { - if (in_array($request->getMethod(), ['PUT', 'POST', 'DELETE', 'PATCH']) || $request->getData()) { + if (in_array($request->getMethod(), ['PUT', 'POST', 'DELETE', 'PATCH'], true) || $request->getData()) { $this->_validateToken($request); $body = $request->getParsedBody(); if (is_array($body)) { @@ -164,13 +196,17 @@ protected function _addTokenCookie($token, ServerRequest $request, Response $res { $expiry = new Time($this->_config['expiry']); - return $response->withCookie($this->_config['cookieName'], [ - 'value' => $token, - 'expire' => $expiry->format('U'), - 'path' => $request->getAttribute('webroot'), - 'secure' => $this->_config['secure'], - 'httpOnly' => $this->_config['httpOnly'], - ]); + $cookie = new Cookie( + $this->_config['cookieName'], + $token, + $expiry, + $request->getAttribute('webroot'), + '', + (bool)$this->_config['secure'], + (bool)$this->_config['httpOnly'] + ); + + return $response->withCookie($cookie); } /** diff --git a/app/vendor/cakephp/cakephp/src/Http/Middleware/SecurityHeadersMiddleware.php b/app/vendor/cakephp/cakephp/src/Http/Middleware/SecurityHeadersMiddleware.php index 2c29f2907..c86ee6484 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Middleware/SecurityHeadersMiddleware.php +++ b/app/vendor/cakephp/cakephp/src/Http/Middleware/SecurityHeadersMiddleware.php @@ -20,9 +20,76 @@ /** * Handles common security headers in a convenient way + * + * @link https://book.cakephp.org/3.0/en/controllers/middleware.html#security-header-middleware */ class SecurityHeadersMiddleware { + /** @var string X-Content-Type-Option nosniff */ + const NOSNIFF = 'nosniff'; + + /** @var string X-Download-Option noopen */ + const NOOPEN = 'noopen'; + + /** @var string Referrer-Policy no-referrer */ + const NO_REFERRER = 'no-referrer'; + + /** @var string Referrer-Policy no-referrer-when-downgrade */ + const NO_REFERRER_WHEN_DOWNGRADE = 'no-referrer-when-downgrade'; + + /** @var string Referrer-Policy origin */ + const ORIGIN = 'origin'; + + /** @var string Referrer-Policy origin-when-cross-origin */ + const ORIGIN_WHEN_CROSS_ORIGIN = 'origin-when-cross-origin'; + + /** @var string Referrer-Policy same-origin */ + const SAME_ORIGIN = 'same-origin'; + + /** @var string Referrer-Policy strict-origin */ + const STRICT_ORIGIN = 'strict-origin'; + + /** @var string Referrer-Policy strict-origin-when-cross-origin */ + const STRICT_ORIGIN_WHEN_CROSS_ORIGIN = 'strict-origin-when-cross-origin'; + + /** @var string Referrer-Policy unsafe-url */ + const UNSAFE_URL = 'unsafe-url'; + + /** @var string X-Frame-Option deny */ + const DENY = 'deny'; + + /** @var string X-Frame-Option sameorigin */ + const SAMEORIGIN = 'sameorigin'; + + /** @var string X-Frame-Option allow-from */ + const ALLOW_FROM = 'allow-from'; + + /** @var string X-XSS-Protection block, sets enabled with block */ + const XSS_BLOCK = 'block'; + + /** @var string X-XSS-Protection enabled with block */ + const XSS_ENABLED_BLOCK = '1; mode=block'; + + /** @var string X-XSS-Protection enabled */ + const XSS_ENABLED = '1'; + + /** @var string X-XSS-Protection disabled */ + const XSS_DISABLED = '0'; + + /** @var string X-Permitted-Cross-Domain-Policy all */ + const ALL = 'all'; + + /** @var string X-Permitted-Cross-Domain-Policy none */ + const NONE = 'none'; + + /** @var string X-Permitted-Cross-Domain-Policy master-only */ + const MASTER_ONLY = 'master-only'; + + /** @var string X-Permitted-Cross-Domain-Policy by-content-type */ + const BY_CONTENT_TYPE = 'by-content-type'; + + /** @var string X-Permitted-Cross-Domain-Policy by-ftp-filename */ + const BY_FTP_FILENAME = 'by-ftp-filename'; /** * Security related headers to set @@ -41,7 +108,7 @@ class SecurityHeadersMiddleware */ public function noSniff() { - $this->headers['x-content-type-options'] = 'nosniff'; + $this->headers['x-content-type-options'] = self::NOSNIFF; return $this; } @@ -56,7 +123,7 @@ public function noSniff() */ public function noOpen() { - $this->headers['x-download-options'] = 'noopen'; + $this->headers['x-download-options'] = self::NOOPEN; return $this; } @@ -65,17 +132,21 @@ public function noOpen() * Referrer-Policy * * @link https://w3c.github.io/webappsec-referrer-policy - * @param string $policy Policy value. Available Value: 'no-referrer', 'no-referrer-when-downgrade', 'origin', 'origin-when-cross-origin', - * 'same-origin', 'strict-origin', 'strict-origin-when-cross-origin', 'unsafe-url' + * @param string $policy Policy value. Available Value: 'no-referrer', 'no-referrer-when-downgrade', 'origin', + * 'origin-when-cross-origin', 'same-origin', 'strict-origin', 'strict-origin-when-cross-origin', 'unsafe-url' * @return $this */ - public function setReferrerPolicy($policy = 'same-origin') + public function setReferrerPolicy($policy = self::SAME_ORIGIN) { $available = [ - 'no-referrer', 'no-referrer-when-downgrade', 'origin', - 'origin-when-cross-origin', - 'same-origin', 'strict-origin', 'strict-origin-when-cross-origin', - 'unsafe-url' + self::NO_REFERRER, + self::NO_REFERRER_WHEN_DOWNGRADE, + self::ORIGIN, + self::ORIGIN_WHEN_CROSS_ORIGIN, + self::SAME_ORIGIN, + self::STRICT_ORIGIN, + self::STRICT_ORIGIN_WHEN_CROSS_ORIGIN, + self::UNSAFE_URL, ]; $this->checkValues($policy, $available); @@ -92,11 +163,11 @@ public function setReferrerPolicy($policy = 'same-origin') * @param string $url URL if mode is `allow-from` * @return $this */ - public function setXFrameOptions($option = 'sameorigin', $url = null) + public function setXFrameOptions($option = self::SAMEORIGIN, $url = null) { - $this->checkValues($option, ['deny', 'sameorigin', 'allow-from']); + $this->checkValues($option, [self::DENY, self::SAMEORIGIN, self::ALLOW_FROM]); - if ($option === 'allow-from') { + if ($option === self::ALLOW_FROM) { if (empty($url)) { throw new InvalidArgumentException('The 2nd arg $url can not be empty when `allow-from` is used'); } @@ -115,15 +186,15 @@ public function setXFrameOptions($option = 'sameorigin', $url = null) * @param string $mode Mode value. Available Values: '1', '0', 'block' * @return $this */ - public function setXssProtection($mode = 'block') + public function setXssProtection($mode = self::XSS_BLOCK) { $mode = (string)$mode; - if ($mode === 'block') { - $mode = '1; mode=block'; + if ($mode === self::XSS_BLOCK) { + $mode = self::XSS_ENABLED_BLOCK; } - $this->checkValues($mode, ['1', '0', '1; mode=block']); + $this->checkValues($mode, [self::XSS_ENABLED, self::XSS_DISABLED, self::XSS_ENABLED_BLOCK]); $this->headers['x-xss-protection'] = $mode; return $this; @@ -133,12 +204,19 @@ public function setXssProtection($mode = 'block') * X-Permitted-Cross-Domain-Policies * * @link https://www.adobe.com/devnet/adobe-media-server/articles/cross-domain-xml-for-streaming.html - * @param string $policy Policy value. Available Values: 'all', 'none', 'master-only', 'by-content-type', 'by-ftp-filename' + * @param string $policy Policy value. Available Values: 'all', 'none', 'master-only', 'by-content-type', + * 'by-ftp-filename' * @return $this */ - public function setCrossDomainPolicy($policy = 'all') + public function setCrossDomainPolicy($policy = self::ALL) { - $this->checkValues($policy, ['all', 'none', 'master-only', 'by-content-type', 'by-ftp-filename']); + $this->checkValues($policy, [ + self::ALL, + self::NONE, + self::MASTER_ONLY, + self::BY_CONTENT_TYPE, + self::BY_FTP_FILENAME, + ]); $this->headers['x-permitted-cross-domain-policies'] = $policy; return $this; @@ -149,7 +227,7 @@ public function setCrossDomainPolicy($policy = 'all') * * @throws \InvalidArgumentException Thrown when a value is invalid. * @param string $value Value to check - * @param array $allowed List of allowed values + * @param string[] $allowed List of allowed values * @return void */ protected function checkValues($value, array $allowed) diff --git a/app/vendor/cakephp/cakephp/src/Http/Response.php b/app/vendor/cakephp/cakephp/src/Http/Response.php index 7777a883f..16b97bd8e 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Response.php +++ b/app/vendor/cakephp/cakephp/src/Http/Response.php @@ -24,6 +24,7 @@ use Cake\Http\Exception\NotFoundException; use Cake\Log\Log; use DateTime; +use DateTimeInterface; use DateTimeZone; use InvalidArgumentException; use Psr\Http\Message\ResponseInterface; @@ -42,7 +43,7 @@ class Response implements ResponseInterface /** * Holds HTTP response statuses * - * @var array + * @var string[] */ protected $_statusCodes = [ 100 => 'Continue', @@ -430,6 +431,7 @@ class Response implements ResponseInterface * - status: the HTTP status code to respond with * - type: a complete mime-type string or an extension mapped in this class * - charset: the charset for the response body + * @throws \InvalidArgumentException */ public function __construct(array $options = []) { @@ -580,7 +582,7 @@ protected function _setContentType() return; } $whitelist = [ - 'application/javascript', 'application/json', 'application/xml', 'application/rss+xml' + 'application/javascript', 'application/xml', 'application/rss+xml' ]; $charset = false; @@ -1103,7 +1105,7 @@ public function httpCodes($code = null) * type(['jpg' => 'text/plain']); * ``` * - * @param string|null $contentType Content type key. + * @param string|array|null $contentType Content type key. * @return mixed Current content type or false if supplied an invalid content type. * @deprecated 3.5.5 Use getType() or withType() instead. */ @@ -1111,7 +1113,7 @@ public function type($contentType = null) { deprecationWarning( 'Response::type() is deprecated. ' . - 'Use getType() or withType() instead.' + 'Use setTypeMap(), getType() or withType() instead.' ); if ($contentType === null) { @@ -1137,6 +1139,22 @@ public function type($contentType = null) return $contentType; } + /** + * Sets a content type definition into the map. + * + * E.g.: setTypeMap('xhtml', ['application/xhtml+xml', 'application/xhtml']) + * + * This is needed for RequestHandlerComponent and recognition of types. + * + * @param string $type Content type. + * @param string|array $mimeType Definition of the mime type. + * @return void + */ + public function setTypeMap($type, $mimeType) + { + $this->_mimeTypes[$type] = $mimeType; + } + /** * Returns the current content type. * @@ -1192,7 +1210,7 @@ protected function resolveType($contentType) * e.g `getMimeType('pdf'); // returns 'application/pdf'` * * @param string $alias the content type alias to map - * @return mixed String mapped mime type or false if $alias is not mapped + * @return string|array|false String mapped mime type or false if $alias is not mapped */ public function getMimeType($alias) { @@ -1308,9 +1326,10 @@ public function withDisabledCache() /** * Sets the correct headers to instruct the client to cache the response. * - * @param string $since a valid time since the response text has not been modified - * @param string $time a valid time for cache expiry + * @param string|int|\DateTimeInterface|null $since a valid time since the response text has not been modified + * @param string|int $time a valid time for cache expiry * @return void + * @throws \InvalidArgumentException * @deprecated 3.4.0 Use withCache() instead. */ public function cache($since, $time = '+1 day') @@ -1322,6 +1341,9 @@ public function cache($since, $time = '+1 day') if (!is_int($time)) { $time = strtotime($time); + if ($time === false) { + throw new InvalidArgumentException('Invalid time parameter. Ensure your time value can be parsed by strtotime'); + } } $this->_setHeader('Date', gmdate('D, j M Y G:i:s ', time()) . 'GMT'); @@ -1335,14 +1357,18 @@ public function cache($since, $time = '+1 day') /** * Create a new instance with the headers to enable client caching. * - * @param string $since a valid time since the response text has not been modified - * @param string $time a valid time for cache expiry + * @param string|int|\DateTimeInterface|null $since A valid time since the response text has not been modified + * @param string|int $time A valid time for cache expiry * @return static + * @throws \InvalidArgumentException */ public function withCache($since, $time = '+1 day') { if (!is_int($time)) { $time = strtotime($time); + if ($time === false) { + throw new InvalidArgumentException('Invalid time parameter. Ensure your time value can be parsed by strtotime'); + } } return $this->withHeader('Date', gmdate('D, j M Y G:i:s ', time()) . 'GMT') @@ -1592,7 +1618,7 @@ protected function _setCacheControl() * `$response->expires(new DateTime('+1 day'))` Will set the expiration in next 24 hours * `$response->expires()` Will return the current expiration header value * - * @param string|\DateTime|null $time Valid time string or \DateTime instance. + * @param string|int|\DateTimeInterface|null $time Valid time string or \DateTime instance. * @return string|null * @deprecated 3.4.0 Use withExpires() instead. */ @@ -1628,7 +1654,7 @@ public function expires($time = null) * $response->withExpires(new DateTime('+1 day')) * ``` * - * @param string|\DateTime $time Valid time string or \DateTime instance. + * @param string|int|\DateTimeInterface|null $time Valid time string or \DateTime instance. * @return static */ public function withExpires($time) @@ -1648,7 +1674,7 @@ public function withExpires($time) * `$response->modified(new DateTime('+1 day'))` Will set the modification date in the past 24 hours * `$response->modified()` Will return the current Last-Modified header value * - * @param string|\DateTime|null $time Valid time string or \DateTime instance. + * @param string|int|\DateTimeInterface|null $time Valid time string or \DateTime instance. * @return string|null * @deprecated 3.4.0 Use withModified() instead. */ @@ -1684,7 +1710,7 @@ public function modified($time = null) * $response->withModified(new DateTime('+1 day')) * ``` * - * @param string|\DateTime $time Valid time string or \DateTime instance. + * @param string|int|\DateTimeInterface|null $time Valid time string or \DateTimeInterface instance. * @return static */ public function withModified($time) @@ -1869,21 +1895,20 @@ public function withEtag($hash, $weak = false) * Returns a DateTime object initialized at the $time param and using UTC * as timezone * - * @param string|int|\DateTime|null $time Valid time string or \DateTime instance. - * @return \DateTime + * @param string|int|\DateTimeInterface|null $time Valid time string or \DateTimeInterface instance. + * @return \DateTimeInterface */ protected function _getUTCDate($time = null) { - if ($time instanceof DateTime) { + if ($time instanceof DateTimeInterface) { $result = clone $time; } elseif (is_int($time)) { $result = new DateTime(date('Y-m-d H:i:s', $time)); } else { $result = new DateTime($time); } - $result->setTimezone(new DateTimeZone('UTC')); - return $result; + return $result->setTimezone(new DateTimeZone('UTC')); } /** @@ -1909,7 +1934,7 @@ public function compress() public function outputCompressed() { return strpos(env('HTTP_ACCEPT_ENCODING'), 'gzip') !== false - && (ini_get('zlib.output_compression') === '1' || in_array('ob_gzhandler', ob_list_handlers())); + && (ini_get('zlib.output_compression') === '1' || in_array('ob_gzhandler', ob_list_handlers(), true)); } /** @@ -2055,19 +2080,20 @@ public function checkNotModified(ServerRequest $request) { $etags = preg_split('/\s*,\s*/', (string)$request->getHeaderLine('If-None-Match'), 0, PREG_SPLIT_NO_EMPTY); $responseTag = $this->getHeaderLine('Etag'); + $etagMatches = null; if ($responseTag) { - $etagMatches = in_array('*', $etags) || in_array($responseTag, $etags); + $etagMatches = in_array('*', $etags, true) || in_array($responseTag, $etags, true); } $modifiedSince = $request->getHeaderLine('If-Modified-Since'); + $timeMatches = null; if ($modifiedSince && $this->hasHeader('Last-Modified')) { $timeMatches = strtotime($this->getHeaderLine('Last-Modified')) === strtotime($modifiedSince); } - $checks = compact('etagMatches', 'timeMatches'); - if (empty($checks)) { + if ($etagMatches === null && $timeMatches === null) { return false; } - $notModified = !in_array(false, $checks, true); + $notModified = $etagMatches !== false && $timeMatches !== false; if ($notModified) { $this->notModified(); } @@ -2175,7 +2201,7 @@ public function cookie($options = null) /** * Create a new response with a cookie set. * - * ### Options + * ### Data * * - `value`: Value of the cookie * - `expire`: Time the cookie expires in @@ -2206,6 +2232,11 @@ public function withCookie($name, $data = '') if ($name instanceof Cookie) { $cookie = $name; } else { + deprecationWarning( + get_called_class() . '::withCookie(string $name, array $data) is deprecated. ' . + 'Pass an instance of \Cake\Http\Cookie\Cookie instead.' + ); + if (!is_array($data)) { $data = ['value' => $data]; } @@ -2267,6 +2298,11 @@ public function withExpiredCookie($name, $options = []) if ($name instanceof CookieInterface) { $cookie = $name->withExpired(); } else { + deprecationWarning( + get_called_class() . '::withExpiredCookie(string $name, array $data) is deprecated. ' . + 'Pass an instance of \Cake\Http\Cookie\Cookie instead.' + ); + $options += [ 'path' => '/', 'domain' => '', @@ -2360,6 +2396,20 @@ public function getCookieCollection() return $this->_cookies; } + /** + * Get a new instance with provided cookie collection. + * + * @param \Cake\Http\Cookie\CookieCollection $cookieCollection Cookie collection to set. + * @return static + */ + public function withCookieCollection(CookieCollection $cookieCollection) + { + $new = clone $this; + $new->_cookies = $cookieCollection; + + return $new; + } + /** * Setup access for origin and methods on cross origin requests * @@ -2708,7 +2758,9 @@ protected function _sendFile($file, $range) } $bufferSize = 8192; - set_time_limit(0); + if (strpos(ini_get('disable_functions'), 'set_time_limit') === false) { + set_time_limit(0); + } session_write_close(); while (!feof($file->handle)) { if (!$this->_isActive()) { diff --git a/app/vendor/cakephp/cakephp/src/Http/ResponseEmitter.php b/app/vendor/cakephp/cakephp/src/Http/ResponseEmitter.php index 9e320bc54..72b60d082 100644 --- a/app/vendor/cakephp/cakephp/src/Http/ResponseEmitter.php +++ b/app/vendor/cakephp/cakephp/src/Http/ResponseEmitter.php @@ -33,6 +33,9 @@ * * - It logs headers sent using CakePHP's logging tools. * - Cookies are emitted using setcookie() to not conflict with ext/session + * - For fastcgi servers with PHP-FPM session_write_close() is called just + * before fastcgi_finish_request() to make sure session data is saved + * correctly (especially on slower session backends). */ class ResponseEmitter implements EmitterInterface { @@ -63,6 +66,7 @@ public function emit(ResponseInterface $response, $maxBufferLength = 8192) } if (function_exists('fastcgi_finish_request')) { + session_write_close(); fastcgi_finish_request(); } } diff --git a/app/vendor/cakephp/cakephp/src/Http/Server.php b/app/vendor/cakephp/cakephp/src/Http/Server.php index 5e2691b03..874d6ec70 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Server.php +++ b/app/vendor/cakephp/cakephp/src/Http/Server.php @@ -167,7 +167,7 @@ public function setRunner(Runner $runner) /** * Get the application's event manager or the global one. * - * @return \Cake\Event\EventManagerInterface + * @return \Cake\Event\EventManager */ public function getEventManager() { diff --git a/app/vendor/cakephp/cakephp/src/Http/ServerRequest.php b/app/vendor/cakephp/cakephp/src/Http/ServerRequest.php index d9cd48bbc..a67191b30 100644 --- a/app/vendor/cakephp/cakephp/src/Http/ServerRequest.php +++ b/app/vendor/cakephp/cakephp/src/Http/ServerRequest.php @@ -88,7 +88,7 @@ class ServerRequest implements ArrayAccess, ServerRequestInterface * The URL string used for the request. * * @var string - * @deprecated 3.6.0 This public property will be removed in 4.0.0. Use getRequestTarget() instead. + * @deprecated 3.6.0 This public property will be removed in 4.0.0. Use getPath() instead. */ protected $url; @@ -125,6 +125,13 @@ class ServerRequest implements ArrayAccess, ServerRequestInterface */ public $trustProxy = false; + /** + * Trusted proxies list + * + * @var string[] + */ + protected $trustedProxies = []; + /** * Contents of php://input * @@ -267,7 +274,7 @@ public static function createFromGlobals() * - `files` Uploaded file data formatted like $_FILES. * - `cookies` Cookies for this request. * - `environment` $_SERVER and $_ENV data. - * - ~~`url`~~ The URL without the base path for the request. This option is deprecated and will be removed in 4.0.0 + * - `url` The URL without the base path for the request. * - `uri` The PSR7 UriInterface object. If null, one will be created. * - `base` The base URL for the request. * - `webroot` The webroot directory for the request. @@ -578,8 +585,24 @@ public function session(Session $session = null) public function clientIp() { if ($this->trustProxy && $this->getEnv('HTTP_X_FORWARDED_FOR')) { - $addresses = explode(',', $this->getEnv('HTTP_X_FORWARDED_FOR')); - $ipaddr = end($addresses); + $addresses = array_map('trim', explode(',', $this->getEnv('HTTP_X_FORWARDED_FOR'))); + $trusted = (count($this->trustedProxies) > 0); + $n = count($addresses); + + if ($trusted) { + $trusted = array_diff($addresses, $this->trustedProxies); + $trusted = (count($trusted) === 1); + } + + if ($trusted) { + return $addresses[0]; + } + + return $addresses[$n - 1]; + } + + if ($this->trustProxy && $this->getEnv('HTTP_X_REAL_IP')) { + $ipaddr = $this->getEnv('HTTP_X_REAL_IP'); } elseif ($this->trustProxy && $this->getEnv('HTTP_CLIENT_IP')) { $ipaddr = $this->getEnv('HTTP_CLIENT_IP'); } else { @@ -589,6 +612,28 @@ public function clientIp() return trim($ipaddr); } + /** + * register trusted proxies + * + * @param string[] $proxies ips list of trusted proxies + * @return void + */ + public function setTrustedProxies(array $proxies) + { + $this->trustedProxies = $proxies; + $this->trustProxy = true; + } + + /** + * Get trusted proxies + * + * @return string[] + */ + public function getTrustedProxies() + { + return $this->trustedProxies; + } + /** * Returns the referer that referred this request. * @@ -638,7 +683,7 @@ public function __call($name, $params) return $this->is(...$params); } - throw new BadMethodCallException(sprintf('Method %s does not exist', $name)); + throw new BadMethodCallException(sprintf('Method "%s()" does not exist', $name)); } /** @@ -898,7 +943,7 @@ protected function _environmentDetector($detect) * See Request::is() for how to add additional types and the * built-in types. * - * @param array $types The types to check. + * @param string[] $types The types to check. * @return bool Success. * @see \Cake\Http\ServerRequest::is() */ @@ -911,16 +956,15 @@ public function isAll(array $types) /** * Add a new detector to the list of detectors that a request can use. - * There are several different formats and types of detectors that can be set. + * There are several different types of detectors that can be set. * - * ### Callback detectors + * ### Callback comparison * * Callback detectors allow you to provide a callable to handle the check. * The callback will receive the request object as its only parameter. * * ``` * addDetector('custom', function ($request) { //Return a boolean }); - * addDetector('custom', ['SomeClass', 'somemethod']); * ``` * * ### Environment value comparison @@ -928,7 +972,36 @@ public function isAll(array $types) * An environment value comparison, compares a value fetched from `env()` to a known value * the environment value is equality checked against the provided value. * - * e.g `addDetector('post', ['env' => 'REQUEST_METHOD', 'value' => 'POST'])` + * ``` + * addDetector('post', ['env' => 'REQUEST_METHOD', 'value' => 'POST']); + * ``` + * + * ### Request parameter comparison + * + * Allows for custom detectors on the request parameters. + * + * ``` + * addDetector('requested', ['param' => 'requested', 'value' => 1]); + * ``` + * + * ### Accept comparison + * + * Allows for detector to compare against Accept header value. + * + * ``` + * addDetector('csv', ['accept' => 'text/csv']); + * ``` + * + * ### Header comparison + * + * Allows for one or more headers to be compared. + * + * ``` + * addDetector('fancy', ['header' => ['X-Fancy' => 1]); + * ``` + * + * The `param`, `env` and comparison types allow the following + * value comparison options: * * ### Pattern value comparison * @@ -947,17 +1020,11 @@ public function isAll(array $types) * addDetector('mobile', ['env' => 'HTTP_USER_AGENT', 'options' => ['Fennec']]); * ``` * - * ### Request parameter detectors - * - * Allows for custom detectors on the request parameters. - * - * e.g `addDetector('requested', ['param' => 'requested', 'value' => 1]` - * - * You can also make parameter detectors that accept multiple values + * You can also make compare against multiple values * using the `options` key. This is useful when you want to check - * if a request parameter is in a list of options. + * if a request value is in a list of options. * - * `addDetector('extension', ['param' => 'ext', 'options' => ['pdf', 'csv']]` + * `addDetector('extension', ['param' => '_ext', 'options' => ['pdf', 'csv']]` * * @param string $name The name of the detector. * @param callable|array $callable A callable or options array for the detector definition. @@ -1493,8 +1560,8 @@ public function acceptLanguage($language = null) protected function _parseAcceptWithQualifier($header) { $accept = []; - $header = explode(',', $header); - foreach (array_filter($header) as $value) { + $headers = explode(',', $header); + foreach (array_filter($headers) as $value) { $prefValue = '1.0'; $value = trim($value); @@ -1812,7 +1879,7 @@ public function withCookieParams(array $cookies) * Get the parsed request body data. * * If the request Content-Type is either application/x-www-form-urlencoded - * or multipart/form-data, nd the request method is POST, this will be the + * or multipart/form-data, and the request method is POST, this will be the * post data. For other content types, it may be the deserialized request * body. * diff --git a/app/vendor/cakephp/cakephp/src/Http/ServerRequestFactory.php b/app/vendor/cakephp/cakephp/src/Http/ServerRequestFactory.php index 3641a9ed3..daaf20f51 100644 --- a/app/vendor/cakephp/cakephp/src/Http/ServerRequestFactory.php +++ b/app/vendor/cakephp/cakephp/src/Http/ServerRequestFactory.php @@ -130,7 +130,7 @@ protected static function updatePath($base, $uri) if (empty($path) || $path === '/' || $path === '//' || $path === '/index.php') { $path = '/'; } - $endsWithIndex = '/webroot/index.php'; + $endsWithIndex = '/' . (Configure::read('App.webroot') ?: 'webroot') . '/index.php'; $endsWithLength = strlen($endsWithIndex); if (strlen($path) >= $endsWithLength && substr($path, -$endsWithLength) === $endsWithIndex diff --git a/app/vendor/cakephp/cakephp/src/Http/Session.php b/app/vendor/cakephp/cakephp/src/Http/Session.php index 3ab2254ff..1f2a94992 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Session.php +++ b/app/vendor/cakephp/cakephp/src/Http/Session.php @@ -228,7 +228,7 @@ public function __construct(array $config = []) $this->engine($class, $config['handler']); } - $this->_lifetime = ini_get('session.gc_maxlifetime'); + $this->_lifetime = (int)ini_get('session.gc_maxlifetime'); $this->_isCLI = (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg'); session_register_shutdown(); } @@ -290,7 +290,7 @@ public function engine($class = null, array $options = []) */ protected function setEngine(SessionHandlerInterface $handler) { - if (!headers_sent()) { + if (!headers_sent() && session_status() !== \PHP_SESSION_ACTIVE) { session_set_save_handler($handler, false); } @@ -368,6 +368,26 @@ public function start() return $this->_started; } + /** + * Write data and close the session + * + * @return bool True if session was started + */ + public function close() + { + if (!$this->_started) { + return true; + } + + if (!session_write_close()) { + throw new RuntimeException('Could not close the session'); + } + + $this->_started = false; + + return true; + } + /** * Determine if Session has already been started. * @@ -535,7 +555,7 @@ public function destroy() $this->start(); } - if (!$this->_isCLI && session_status() === PHP_SESSION_ACTIVE) { + if (!$this->_isCLI && session_status() === \PHP_SESSION_ACTIVE) { session_destroy(); } diff --git a/app/vendor/cakephp/cakephp/src/Http/Session/CacheSession.php b/app/vendor/cakephp/cakephp/src/Http/Session/CacheSession.php index 739b062ad..250567f2a 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Session/CacheSession.php +++ b/app/vendor/cakephp/cakephp/src/Http/Session/CacheSession.php @@ -23,7 +23,7 @@ /** * CacheSession provides method for saving sessions into a Cache engine. Used with Session * - * @see \Cake\Model\Datasource\Session for configuration information. + * @see \Cake\Http\Session for configuration information. */ class CacheSession implements SessionHandlerInterface { diff --git a/app/vendor/cakephp/cakephp/src/Http/Session/DatabaseSession.php b/app/vendor/cakephp/cakephp/src/Http/Session/DatabaseSession.php index 914da8381..0b1433e97 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Session/DatabaseSession.php +++ b/app/vendor/cakephp/cakephp/src/Http/Session/DatabaseSession.php @@ -62,7 +62,7 @@ public function __construct(array $config = []) $this->_table = $tableLocator->get($config['model']); } - $this->_timeout = ini_get('session.gc_maxlifetime'); + $this->_timeout = (int)ini_get('session.gc_maxlifetime'); } /** @@ -114,7 +114,7 @@ public function read($id) ->find('all') ->select(['data']) ->where([$this->_table->getPrimaryKey() => $id]) - ->enableHydration(false) + ->disableHydration() ->first(); if (empty($result)) { diff --git a/app/vendor/cakephp/cakephp/src/I18n/Date.php b/app/vendor/cakephp/cakephp/src/I18n/Date.php index a02172481..4f5e3ed16 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/Date.php +++ b/app/vendor/cakephp/cakephp/src/I18n/Date.php @@ -29,7 +29,7 @@ class Date extends MutableDate implements JsonSerializable /** * The format to use when formatting a time using `Cake\I18n\Date::i18nFormat()` - * and `__toString` + * and `__toString`. This format is also used by `parseDateTime()`. * * The format should be either the formatting constants from IntlDateFormatter as * described in (https://secure.php.net/manual/en/class.intldateformatter.php) or a pattern @@ -73,7 +73,7 @@ class Date extends MutableDate implements JsonSerializable * The format to use when formatting a time using `Date::timeAgoInWords()` * and the difference is less than `Date::$wordEnd` * - * @var array + * @var string[] * @see \Cake\I18n\Date::timeAgoInWords() */ public static $wordAccuracy = [ diff --git a/app/vendor/cakephp/cakephp/src/I18n/DateFormatTrait.php b/app/vendor/cakephp/cakephp/src/I18n/DateFormatTrait.php index d2dbc42ff..ba34a505a 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/DateFormatTrait.php +++ b/app/vendor/cakephp/cakephp/src/I18n/DateFormatTrait.php @@ -56,7 +56,7 @@ trait DateFormatTrait * @var string|array|int * @see \Cake\I18n\Time::i18nFormat() */ - protected static $_jsonEncodeFormat = "yyyy-MM-dd'T'HH:mm:ssxxx"; + protected static $_jsonEncodeFormat = "yyyy-MM-dd'T'HH':'mm':'ssxxx"; /** * Caches whether or not this class is a subclass of a Date or MutableDate @@ -125,9 +125,7 @@ public function nice($timezone = null, $locale = null) * $time->i18nFormat(Time::UNIX_TIMESTAMP_FORMAT); // outputs '1398031800' * ``` * - * If you wish to control the default format to be used for this method, you can alter - * the value of the static `Time::$defaultLocale` variable and set it to one of the - * possible formats accepted by this function. + * You can control the default format used through `Time::setToStringFormat()`. * * You can read about the available IntlDateFormatter constants at * https://secure.php.net/manual/en/class.intldateformatter.php @@ -147,16 +145,15 @@ public function nice($timezone = null, $locale = null) * $time->i18nFormat(\IntlDateFormatter::FULL, 'Europe/Berlin', 'de-DE'); * ``` * - * You can control the default locale to be used by setting the static variable - * `Time::$defaultLocale` to a valid locale string. If empty, the default will be - * taken from the `intl.default_locale` ini config. + * You can control the default locale used through `Time::setDefaultLocale()`. + * If empty, the default will be taken from the `intl.default_locale` ini config. * * @param string|int|null $format Format string. * @param string|\DateTimeZone|null $timezone Timezone string or DateTimeZone object * in which the date will be displayed. The timezone stored for this object will not * be changed. * @param string|null $locale The locale name in which the date should be displayed (e.g. pt-BR) - * @return string Formatted and translated date string + * @return string|int Formatted and translated date string */ public function i18nFormat($format = null, $timezone = null, $locale = null) { @@ -184,7 +181,7 @@ public function i18nFormat($format = null, $timezone = null, $locale = null) * * @param \DateTime $date Date. * @param string|int|array $format Format. - * @param string $locale The locale name in which the date should be displayed. + * @param string|null $locale The locale name in which the date should be displayed. * @return string */ protected function _formatObject($date, $format, $locale) @@ -206,6 +203,10 @@ protected function _formatObject($date, $format, $locale) $calendar = IntlDateFormatter::GREGORIAN; } + if ($locale === null) { + $locale = I18n::getLocale(); + } + $timezone = $date->getTimezone()->getName(); $key = "{$locale}.{$dateFormat}.{$timeFormat}.{$timezone}.{$calendar}.{$pattern}"; @@ -257,6 +258,14 @@ public static function resetToStringFormat() /** * Sets the default format used when type converting instances of this type to string * + * The format should be either the formatting constants from IntlDateFormatter as + * described in (https://secure.php.net/manual/en/class.intldateformatter.php) or a pattern + * as specified in (http://www.icu-project.org/apiref/icu4c/classSimpleDateFormat.html#details) + * + * It is possible to provide an array of 2 constants. In this case, the first position + * will be used for formatting the date part of the object and the second position + * will be used to format the time part. + * * @param string|array|int $format Format. * @return void */ @@ -268,6 +277,15 @@ public static function setToStringFormat($format) /** * Sets the default format used when converting this object to json * + * The format should be either the formatting constants from IntlDateFormatter as + * described in (https://secure.php.net/manual/en/class.intldateformatter.php) or a pattern + * as specified in (http://www.icu-project.org/apiref/icu4c/classSimpleDateFormat.html#details) + * + * It is possible to provide an array of 2 constants. In this case, the first position + * will be used for formatting the date part of the object and the second position + * will be used to format the time part. + * + * @see \Cake\I18n\Time::i18nFormat() * @param string|array|int $format Format. * @return void */ diff --git a/app/vendor/cakephp/cakephp/src/I18n/FrozenDate.php b/app/vendor/cakephp/cakephp/src/I18n/FrozenDate.php index 471d17799..733ab425f 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/FrozenDate.php +++ b/app/vendor/cakephp/cakephp/src/I18n/FrozenDate.php @@ -31,7 +31,7 @@ class FrozenDate extends ChronosDate implements JsonSerializable /** * The format to use when formatting a time using `Cake\I18n\Date::i18nFormat()` - * and `__toString` + * and `__toString`. This format is also used by `parseDateTime()`. * * The format should be either the formatting constants from IntlDateFormatter as * described in (https://secure.php.net/manual/en/class.intldateformatter.php) or a pattern @@ -75,7 +75,7 @@ class FrozenDate extends ChronosDate implements JsonSerializable * The format to use when formatting a time using `Date::timeAgoInWords()` * and the difference is less than `Date::$wordEnd` * - * @var array + * @var string[] * @see \Cake\I18n\Date::timeAgoInWords() */ public static $wordAccuracy = [ diff --git a/app/vendor/cakephp/cakephp/src/I18n/FrozenTime.php b/app/vendor/cakephp/cakephp/src/I18n/FrozenTime.php index 500aa216e..7181d3fdc 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/FrozenTime.php +++ b/app/vendor/cakephp/cakephp/src/I18n/FrozenTime.php @@ -32,7 +32,7 @@ class FrozenTime extends Chronos implements JsonSerializable /** * The format to use when formatting a time using `Cake\I18n\FrozenTime::i18nFormat()` - * and `__toString` + * and `__toString`. This format is also used by `parseDateTime()`. * * The format should be either the formatting constants from IntlDateFormatter as * described in (https://secure.php.net/manual/en/class.intldateformatter.php) or a pattern @@ -76,7 +76,7 @@ class FrozenTime extends Chronos implements JsonSerializable * The format to use when formatting a time using `Time::timeAgoInWords()` * and the difference is less than `Time::$wordEnd` * - * @var array + * @var string[] * @see \Cake\I18n\FrozenTime::timeAgoInWords() */ public static $wordAccuracy = [ @@ -241,7 +241,7 @@ public static function listTimezones($filter = null, $country = null, $options = } /** - * Returns true this instance will happen within the specified interval + * Returns true this instance happened within the specified interval * * This overridden method provides backwards compatible behavior for integers, * or strings with trailing spaces. This behavior is *deprecated* and will be @@ -266,7 +266,7 @@ public function wasWithinLast($timeInterval) } /** - * Returns true this instance happened within the specified interval + * Returns true this instance will happen within the specified interval * * This overridden method provides backwards compatible behavior for integers, * or strings with trailing spaces. This behavior is *deprecated* and will be diff --git a/app/vendor/cakephp/cakephp/src/I18n/MessagesFileLoader.php b/app/vendor/cakephp/cakephp/src/I18n/MessagesFileLoader.php index 3fb0d60bf..33cb7c7b0 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/MessagesFileLoader.php +++ b/app/vendor/cakephp/cakephp/src/I18n/MessagesFileLoader.php @@ -171,7 +171,7 @@ public function translationsFolders() // If space is not added after slash, the character after it remains lowercased $pluginName = Inflector::camelize(str_replace('/', '/ ', $this->_name)); - if (Plugin::loaded($pluginName)) { + if (Plugin::isLoaded($pluginName)) { $basePath = Plugin::classPath($pluginName) . 'Locale' . DIRECTORY_SEPARATOR; foreach ($folders as $folder) { $searchPaths[] = $basePath . $folder . DIRECTORY_SEPARATOR; diff --git a/app/vendor/cakephp/cakephp/src/I18n/Middleware/LocaleSelectorMiddleware.php b/app/vendor/cakephp/cakephp/src/I18n/Middleware/LocaleSelectorMiddleware.php index 421fe85f6..9d2e2c360 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/Middleware/LocaleSelectorMiddleware.php +++ b/app/vendor/cakephp/cakephp/src/I18n/Middleware/LocaleSelectorMiddleware.php @@ -55,7 +55,10 @@ public function __invoke(ServerRequestInterface $request, ResponseInterface $res if (!$locale) { return $next($request, $response); } - if (in_array($locale, $this->locales) || $this->locales === ['*']) { + if ($this->locales !== ['*']) { + $locale = Locale::lookup($this->locales, $locale, true); + } + if ($locale || $this->locales === ['*']) { I18n::setLocale($locale); } diff --git a/app/vendor/cakephp/cakephp/src/I18n/Number.php b/app/vendor/cakephp/cakephp/src/I18n/Number.php index 0027bd9ed..eaba2d444 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/Number.php +++ b/app/vendor/cakephp/cakephp/src/I18n/Number.php @@ -128,7 +128,7 @@ public static function toPercentage($value, $precision = 2, array $options = []) * * - `places` - Minimum number or decimals to use, e.g 0 * - `precision` - Maximum Number of decimal places to use, e.g. 2 - * - `pattern` - An ICU number pattern to use for formatting the number. e.g #,###.00 + * - `pattern` - An ICU number pattern to use for formatting the number. e.g #,##0.00 * - `locale` - The locale name to use for formatting the number, e.g. fr_FR * - `before` - The string to place before whole numbers, e.g. '[' * - `after` - The string to place after decimal numbers, e.g. ']' @@ -204,7 +204,7 @@ public static function formatDelta($value, array $options = []) * - `zero` - The text to use for zero values, can be a string or a number. e.g. 0, 'Free!' * - `places` - Number of decimal places to use. e.g. 2 * - `precision` - Maximum Number of decimal places to use, e.g. 2 - * - `pattern` - An ICU number pattern to use for formatting the number. e.g #,###.00 + * - `pattern` - An ICU number pattern to use for formatting the number. e.g #,##0.00 * - `useIntlCode` - Whether or not to replace the currency symbol with the international * currency code. * @@ -277,7 +277,7 @@ public static function defaultCurrency($currency = null) * numbers representing money or a NumberFormatter constant. * - `places` - Number of decimal places to use. e.g. 2 * - `precision` - Maximum Number of decimal places to use, e.g. 2 - * - `pattern` - An ICU number pattern to use for formatting the number. e.g #,###.00 + * - `pattern` - An ICU number pattern to use for formatting the number. e.g #,##0.00 * - `useIntlCode` - Whether or not to replace the currency symbol with the international * currency code. * diff --git a/app/vendor/cakephp/cakephp/src/I18n/Parser/MoFileParser.php b/app/vendor/cakephp/cakephp/src/I18n/Parser/MoFileParser.php index c8037e077..f7a11b73c 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/Parser/MoFileParser.php +++ b/app/vendor/cakephp/cakephp/src/I18n/Parser/MoFileParser.php @@ -134,9 +134,9 @@ public function parse($resource) continue; } - $messages[$singularId] = $singular; + $messages[$singularId]['_context'][''] = $singular; if ($pluralId !== null) { - $messages[$pluralId] = $plurals; + $messages[$pluralId]['_context'][''] = $plurals; } } diff --git a/app/vendor/cakephp/cakephp/src/I18n/PluralRules.php b/app/vendor/cakephp/cakephp/src/I18n/PluralRules.php index 8a420ce7c..0e4f66630 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/PluralRules.php +++ b/app/vendor/cakephp/cakephp/src/I18n/PluralRules.php @@ -116,7 +116,7 @@ class PluralRules 'th' => 0, 'ti' => 2, 'tk' => 1, - 'tr' => 0, + 'tr' => 1, 'uk' => 3, 'ur' => 1, 'vi' => 0, diff --git a/app/vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php b/app/vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php index 8f4b714c7..d921f7812 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php +++ b/app/vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php @@ -197,27 +197,27 @@ protected function _diffData($futureTime, $pastTime, $backwards, $options) list($past['H'], $past['i'], $past['s'], $past['d'], $past['m'], $past['Y']) = explode('/', date('H/i/s/d/m/Y', $pastTime)); $weeks = $days = $hours = $minutes = $seconds = 0; - $years = $future['Y'] - $past['Y']; - $months = $future['m'] + ((12 * $years) - $past['m']); + $years = (int)$future['Y'] - (int)$past['Y']; + $months = (int)$future['m'] + ((12 * $years) - (int)$past['m']); if ($months >= 12) { $years = floor($months / 12); $months -= ($years * 12); } - if ($future['m'] < $past['m'] && $future['Y'] - $past['Y'] === 1) { + if ((int)$future['m'] < (int)$past['m'] && (int)$future['Y'] - (int)$past['Y'] === 1) { $years--; } - if ($future['d'] >= $past['d']) { - $days = $future['d'] - $past['d']; + if ((int)$future['d'] >= (int)$past['d']) { + $days = (int)$future['d'] - (int)$past['d']; } else { - $daysInPastMonth = date('t', $pastTime); - $daysInFutureMonth = date('t', mktime(0, 0, 0, $future['m'] - 1, 1, $future['Y'])); + $daysInPastMonth = (int)date('t', $pastTime); + $daysInFutureMonth = (int)date('t', mktime(0, 0, 0, (int)$future['m'] - 1, 1, (int)$future['Y'])); if (!$backwards) { - $days = ($daysInPastMonth - $past['d']) + $future['d']; + $days = ($daysInPastMonth - (int)$past['d']) + (int)$future['d']; } else { - $days = ($daysInFutureMonth - $past['d']) + $future['d']; + $days = ($daysInFutureMonth - (int)$past['d']) + (int)$future['d']; } if ($future['m'] != $past['m']) { diff --git a/app/vendor/cakephp/cakephp/src/I18n/Time.php b/app/vendor/cakephp/cakephp/src/I18n/Time.php index 0d05665df..e13af084f 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/Time.php +++ b/app/vendor/cakephp/cakephp/src/I18n/Time.php @@ -30,7 +30,7 @@ class Time extends MutableDateTime implements JsonSerializable /** * The format to use when formatting a time using `Cake\I18n\Time::i18nFormat()` - * and `__toString` + * and `__toString`. This format is also used by `parseDateTime()`. * * The format should be either the formatting constants from IntlDateFormatter as * described in (https://secure.php.net/manual/en/class.intldateformatter.php) or a pattern @@ -74,7 +74,7 @@ class Time extends MutableDateTime implements JsonSerializable * The format to use when formatting a time using `Time::timeAgoInWords()` * and the difference is less than `Time::$wordEnd` * - * @var array + * @var string[] * @see \Cake\I18n\Time::timeAgoInWords() */ public static $wordAccuracy = [ @@ -172,7 +172,7 @@ public function isThisYear() */ public function toQuarter($range = false) { - $quarter = ceil($this->format('m') / 3); + $quarter = (int)ceil($this->format('m') / 3); if ($range === false) { return $quarter; } diff --git a/app/vendor/cakephp/cakephp/src/Log/Engine/BaseLog.php b/app/vendor/cakephp/cakephp/src/Log/Engine/BaseLog.php index c35e53363..1aca5140e 100644 --- a/app/vendor/cakephp/cakephp/src/Log/Engine/BaseLog.php +++ b/app/vendor/cakephp/cakephp/src/Log/Engine/BaseLog.php @@ -21,12 +21,9 @@ /** * Base log engine class. - * - * @mixin \Cake\Core\InstanceConfigTrait */ abstract class BaseLog extends AbstractLogger { - use InstanceConfigTrait; /** diff --git a/app/vendor/cakephp/cakephp/src/Log/Engine/ConsoleLog.php b/app/vendor/cakephp/cakephp/src/Log/Engine/ConsoleLog.php index db8e72ba0..4d9e71176 100644 --- a/app/vendor/cakephp/cakephp/src/Log/Engine/ConsoleLog.php +++ b/app/vendor/cakephp/cakephp/src/Log/Engine/ConsoleLog.php @@ -32,7 +32,7 @@ class ConsoleLog extends BaseLog 'stream' => 'php://stderr', 'levels' => null, 'scopes' => [], - 'outputAs' => 'see constructor' + 'outputAs' => null, ]; /** @@ -57,14 +57,6 @@ class ConsoleLog extends BaseLog */ public function __construct(array $config = []) { - if ((DIRECTORY_SEPARATOR === '\\' && !(bool)env('ANSICON') && env('ConEmuANSI') !== 'ON') || - (function_exists('posix_isatty') && !posix_isatty($this->_output)) - ) { - $this->_defaultConfig['outputAs'] = ConsoleOutput::PLAIN; - } else { - $this->_defaultConfig['outputAs'] = ConsoleOutput::COLOR; - } - parent::__construct($config); $config = $this->_config; @@ -75,7 +67,10 @@ public function __construct(array $config = []) } else { throw new InvalidArgumentException('`stream` not a ConsoleOutput nor string'); } - $this->_output->setOutputAs($config['outputAs']); + + if (isset($config['outputAs'])) { + $this->_output->setOutputAs($config['outputAs']); + } } /** diff --git a/app/vendor/cakephp/cakephp/src/Mailer/AbstractTransport.php b/app/vendor/cakephp/cakephp/src/Mailer/AbstractTransport.php index 908b15d29..7fca94e49 100644 --- a/app/vendor/cakephp/cakephp/src/Mailer/AbstractTransport.php +++ b/app/vendor/cakephp/cakephp/src/Mailer/AbstractTransport.php @@ -18,12 +18,9 @@ /** * Abstract transport for sending email - * - * @mixin \Cake\Core\InstanceConfigTrait */ abstract class AbstractTransport { - use InstanceConfigTrait; /** @@ -65,7 +62,10 @@ protected function _headersToString($headers, $eol = "\r\n") if ($value === false || $value === null || $value === '') { continue; } - $out .= $key . ': ' . $value . $eol; + + foreach ((array)$value as $val) { + $out .= $key . ': ' . $val . $eol; + } } if (!empty($out)) { $out = substr($out, 0, -1 * strlen($eol)); diff --git a/app/vendor/cakephp/cakephp/src/Mailer/Email.php b/app/vendor/cakephp/cakephp/src/Mailer/Email.php index 87c16dcae..d9a9807c1 100644 --- a/app/vendor/cakephp/cakephp/src/Mailer/Email.php +++ b/app/vendor/cakephp/cakephp/src/Mailer/Email.php @@ -15,7 +15,6 @@ namespace Cake\Mailer; use BadMethodCallException; -use Cake\Core\App; use Cake\Core\Configure; use Cake\Core\StaticConfigTrait; use Cake\Filesystem\File; @@ -293,22 +292,13 @@ class Email implements JsonSerializable, Serializable protected $_priority; /** - * An array mapping url schemes to fully qualified Transport class names + * An array mapping url schemes to fully qualified Transport class names. + * Unused. * * @var array + * @deprecated 3.7.0 This property is unused and will be removed in 4.0.0. */ - protected static $_dsnClassMap = [ - 'debug' => 'Cake\Mailer\Transport\DebugTransport', - 'mail' => 'Cake\Mailer\Transport\MailTransport', - 'smtp' => 'Cake\Mailer\Transport\SmtpTransport', - ]; - - /** - * Configuration profiles for transports. - * - * @var array - */ - protected static $_transportConfig = []; + protected static $_dsnClassMap = []; /** * A copy of the configuration profile for this @@ -1136,7 +1126,7 @@ public function setHeaders(array $headers) */ public function addHeaders(array $headers) { - $this->_headers = array_merge($this->_headers, $headers); + $this->_headers = Hash::merge($this->_headers, $headers); return $this; } @@ -1205,10 +1195,10 @@ public function getHeaders(array $include = []) } if ($this->_messageId !== false) { if ($this->_messageId === true) { - $headers['Message-ID'] = '<' . str_replace('-', '', Text::uuid()) . '@' . $this->_domain . '>'; - } else { - $headers['Message-ID'] = $this->_messageId; + $this->_messageId = '<' . str_replace('-', '', Text::uuid()) . '@' . $this->_domain . '>'; } + + $headers['Message-ID'] = $this->_messageId; } if ($this->_priority) { @@ -1270,6 +1260,10 @@ protected function _formatAddress($address) */ public function setTemplate($template) { + deprecationWarning( + 'Email::setTemplate() is deprecated. Use $email->viewBuilder()->setTemplate() instead.' + ); + $this->viewBuilder()->setTemplate($template ?: ''); return $this; @@ -1282,6 +1276,10 @@ public function setTemplate($template) */ public function getTemplate() { + deprecationWarning( + 'Email::getTemplate() is deprecated. Use $email->viewBuilder()->getTemplate() instead.' + ); + return $this->viewBuilder()->getTemplate(); } @@ -1290,9 +1288,14 @@ public function getTemplate() * * @param string|null $layout Layout name or null to not use * @return $this + * @deprecated 3.7.0 Use $email->viewBuilder()->setLayout() instead. */ public function setLayout($layout) { + deprecationWarning( + 'Email::setLayout() is deprecated. Use $email->viewBuilder()->setLayout() instead.' + ); + $this->viewBuilder()->setLayout($layout ?: false); return $this; @@ -1301,10 +1304,15 @@ public function setLayout($layout) /** * Gets layout. * + * @deprecated 3.7.0 Use $email->viewBuilder()->getLayout() instead. * @return string */ public function getLayout() { + deprecationWarning( + 'Email::getLayout() is deprecated. Use $email->viewBuilder()->getLayout() instead.' + ); + return $this->viewBuilder()->getLayout(); } @@ -1318,7 +1326,11 @@ public function getLayout() */ public function template($template = false, $layout = false) { - deprecationWarning('Email::template() is deprecated. Use Email::setTemplate() or Email::getTemplate() and Email::setLayout() or Email::getLayout() instead.'); + deprecationWarning( + 'Email::template() is deprecated. ' . + 'Use $email->viewBuilder()->getTemplate()/setTemplate() ' . + 'and $email->viewBuilder()->getLayout()/setLayout() instead.' + ); if ($template === false) { return [ @@ -1422,9 +1434,14 @@ public function viewVars($viewVars = null) * * @param string $theme Theme name. * @return $this + * @deprecated 3.7.0 Use $email->viewBuilder()->setTheme() instead. */ public function setTheme($theme) { + deprecationWarning( + 'Email::setTheme() is deprecated. Use $email->viewBuilder()->setTheme() instead.' + ); + $this->viewBuilder()->setTheme($theme); return $this; @@ -1434,9 +1451,14 @@ public function setTheme($theme) * Gets theme to use when rendering. * * @return string + * @deprecated 3.7.0 Use $email->viewBuilder()->getTheme() instead. */ public function getTheme() { + deprecationWarning( + 'Email::getTheme() is deprecated. Use $email->viewBuilder()->getTheme() instead.' + ); + return $this->viewBuilder()->getTheme(); } @@ -1449,7 +1471,9 @@ public function getTheme() */ public function theme($theme = null) { - deprecationWarning('Email::theme() is deprecated. Use Email::setTheme() or Email::getTheme() instead.'); + deprecationWarning( + 'Email::theme() is deprecated. Use $email->viewBuilder()->getTheme()/setTheme() instead.' + ); if ($theme === null) { return $this->getTheme(); @@ -1463,9 +1487,14 @@ public function theme($theme = null) * * @param array $helpers Helpers list. * @return $this + * @deprecated 3.7.0 Use $email->viewBuilder()->setHelpers() instead. */ public function setHelpers(array $helpers) { + deprecationWarning( + 'Email::setHelpers() is deprecated. Use $email->viewBuilder()->setHelpers() instead.' + ); + $this->viewBuilder()->setHelpers($helpers, false); return $this; @@ -1475,9 +1504,14 @@ public function setHelpers(array $helpers) * Gets helpers to be used when rendering. * * @return array + * @deprecated 3.7.0 Use $email->viewBuilder()->getHelpers() instead. */ public function getHelpers() { + deprecationWarning( + 'Email::getHelpers() is deprecated. Use $email->viewBuilder()->getHelpers() instead.' + ); + return $this->viewBuilder()->getHelpers(); } @@ -1490,7 +1524,9 @@ public function getHelpers() */ public function helpers($helpers = null) { - deprecationWarning('Email::helpers() is deprecated. Use Email::setHelpers() or Email::getHelpers() instead.'); + deprecationWarning( + 'Email::helpers() is deprecated. Use $email->viewBuilder()->getHelpers()/setHelpers() instead.' + ); if ($helpers === null) { return $this->getHelpers(); @@ -1560,7 +1596,7 @@ public function emailFormat($format = null) public function setTransport($name) { if (is_string($name)) { - $transport = $this->_constructTransport($name); + $transport = TransportFactory::get($name); } elseif (is_object($name)) { $transport = $name; } else { @@ -1611,61 +1647,6 @@ public function transport($name = null) return $this->setTransport($name); } - /** - * Build a transport instance from configuration data. - * - * @param string $name The transport configuration name to build. - * @return \Cake\Mailer\AbstractTransport - * @throws \InvalidArgumentException When transport configuration is missing or invalid. - */ - protected function _constructTransport($name) - { - if (!isset(static::$_transportConfig[$name])) { - throw new InvalidArgumentException(sprintf('Transport config "%s" is missing.', $name)); - } - - if (!isset(static::$_transportConfig[$name]['className'])) { - throw new InvalidArgumentException( - sprintf('Transport config "%s" is invalid, the required `className` option is missing', $name) - ); - } - - $config = static::$_transportConfig[$name]; - - if (is_object($config['className'])) { - if (!$config['className'] instanceof AbstractTransport) { - throw new InvalidArgumentException(sprintf( - 'Transport object must be of type "AbstractTransport". Found invalid type: "%s".', - get_class($config['className']) - )); - } - - return $config['className']; - } - - $className = App::className($config['className'], 'Mailer/Transport', 'Transport'); - if (!$className) { - $className = App::className($config['className'], 'Network/Email', 'Transport'); - if ($className) { - trigger_error( - 'Transports in "Network/Email" are deprecated, use "Mailer/Transport" instead.', - E_USER_DEPRECATED - ); - } - } - - if (!$className) { - throw new InvalidArgumentException(sprintf('Transport class "%s" not found.', $config['className'])); - } - if (!method_exists($className, 'send')) { - throw new InvalidArgumentException(sprintf('The "%s" does not have a send() method.', $className)); - } - - unset($config['className']); - - return new $className($config); - } - /** * Sets message ID. * @@ -1994,33 +1975,13 @@ public function getPriority() * @param array|\Cake\Mailer\AbstractTransport|null $config Either an array of configuration * data, or a transport instance. Null when using key as array. * @return void - * @throws \BadMethodCallException When modifying an existing configuration. + * @deprecated 3.7.0 Use TransportFactory::setConfig() instead. */ public static function setConfigTransport($key, $config = null) { - if (is_array($key)) { - foreach ($key as $name => $settings) { - static::setConfigTransport($name, $settings); - } - - return; - } - - if (isset(static::$_transportConfig[$key])) { - throw new BadMethodCallException(sprintf('Cannot modify an existing config "%s"', $key)); - } - - if (is_object($config)) { - $config = ['className' => $config]; - } - - if (isset($config['url'])) { - $parsed = static::parseDsn($config['url']); - unset($config['url']); - $config = $parsed + $config; - } + deprecationWarning('Email::setConfigTransport() is deprecated. Use TransportFactory::setConfig() instead.'); - static::$_transportConfig[$key] = $config; + TransportFactory::setConfig($key, $config); } /** @@ -2028,10 +1989,13 @@ public static function setConfigTransport($key, $config = null) * * @param string $key The configuration name to read. * @return array|null Transport config. + * @deprecated 3.7.0 Use TransportFactory::getConfig() instead. */ public static function getConfigTransport($key) { - return isset(static::$_transportConfig[$key]) ? static::$_transportConfig[$key] : null; + deprecationWarning('Email::getConfigTransport() is deprecated. Use TransportFactory::getConfig() instead.'); + + return TransportFactory::getConfig($key); } /** @@ -2048,7 +2012,7 @@ public static function getConfigTransport($key) * The `className` is used to define the class to use for a transport. * It can either be a short name, or a fully qualified classname * - * @deprecated 3.4.0 Use setConfigTransport()/getConfigTransport() instead. + * @deprecated 3.4.0 Use TransportFactory::setConfig()/getConfig() instead. * @param string|array $key The configuration name to read/write. Or * an array of multiple transports to set. * @param array|\Cake\Mailer\AbstractTransport|null $config Either an array of configuration @@ -2058,28 +2022,31 @@ public static function getConfigTransport($key) */ public static function configTransport($key, $config = null) { - deprecationWarning('Email::configTransport() is deprecated. Use Email::setConfigTransport() or Email::getConfigTransport() instead.'); + deprecationWarning('Email::configTransport() is deprecated. Use TransportFactory::setConfig() or TransportFactory::getConfig() instead.'); if ($config === null && is_string($key)) { - return static::getConfigTransport($key); + return TransportFactory::getConfig($key); } if ($config === null && is_array($key)) { - static::setConfigTransport($key); + TransportFactory::setConfig($key); return null; } - static::setConfigTransport($key, $config); + TransportFactory::setConfig($key, $config); } /** * Returns an array containing the named transport configurations * * @return array Array of configurations. + * @deprecated 3.7.0 Use TransportFactory::configured() instead. */ public static function configuredTransport() { - return array_keys(static::$_transportConfig); + deprecationWarning('Email::configuredTransport() is deprecated. Use TransportFactory::configured().'); + + return TransportFactory::configured(); } /** @@ -2087,10 +2054,13 @@ public static function configuredTransport() * * @param string $key The transport name to remove. * @return void + * @deprecated 3.7.0 Use TransportFactory::drop() instead. */ public static function dropTransport($key) { - unset(static::$_transportConfig[$key]); + deprecationWarning('Email::dropTransport() is deprecated. Use TransportFactory::drop().'); + + TransportFactory::drop($key); } /** @@ -2739,9 +2709,9 @@ protected function _renderTemplates($content) list($templatePlugin) = pluginSplit($View->getTemplate()); list($layoutPlugin) = pluginSplit($View->getLayout()); if ($templatePlugin) { - $View->plugin = $templatePlugin; + $View->setPlugin($templatePlugin); } elseif ($layoutPlugin) { - $View->plugin = $layoutPlugin; + $View->setPlugin($layoutPlugin); } if ($View->get('content') === null) { diff --git a/app/vendor/cakephp/cakephp/src/Mailer/Mailer.php b/app/vendor/cakephp/cakephp/src/Mailer/Mailer.php index 7cafb8826..38c301b4e 100644 --- a/app/vendor/cakephp/cakephp/src/Mailer/Mailer.php +++ b/app/vendor/cakephp/cakephp/src/Mailer/Mailer.php @@ -79,82 +79,85 @@ * Our mailer could either be registered in the application bootstrap, or * in the Table class' initialize() hook. * - * @method \Cake\Mailer\Email setTo($email, $name = null) + * @method \Cake\Mailer\Mailer setTo($email, $name = null) * @method array getTo() - * @method \Cake\Mailer\Email to($email = null, $name = null) - * @method \Cake\Mailer\Email setFrom($email, $name = null) + * @method \Cake\Mailer\Mailer to($email = null, $name = null) + * @method \Cake\Mailer\Mailer setFrom($email, $name = null) * @method array getFrom() - * @method \Cake\Mailer\Email from($email = null, $name = null) - * @method \Cake\Mailer\Email setSender($email, $name = null) + * @method \Cake\Mailer\Mailer from($email = null, $name = null) + * @method \Cake\Mailer\Mailer setSender($email, $name = null) * @method array getSender() - * @method \Cake\Mailer\Email sender($email = null, $name = null) - * @method \Cake\Mailer\Email setReplyTo($email, $name = null) + * @method \Cake\Mailer\Mailer sender($email = null, $name = null) + * @method \Cake\Mailer\Mailer setReplyTo($email, $name = null) * @method array getReplyTo() - * @method \Cake\Mailer\Email replyTo($email = null, $name = null) - * @method \Cake\Mailer\Email setReadReceipt($email, $name = null) + * @method \Cake\Mailer\Mailer replyTo($email = null, $name = null) + * @method \Cake\Mailer\Mailer setReadReceipt($email, $name = null) * @method array getReadReceipt() - * @method \Cake\Mailer\Email readReceipt($email = null, $name = null) - * @method \Cake\Mailer\Email setReturnPath($email, $name = null) + * @method \Cake\Mailer\Mailer readReceipt($email = null, $name = null) + * @method \Cake\Mailer\Mailer setReturnPath($email, $name = null) * @method array getReturnPath() - * @method \Cake\Mailer\Email returnPath($email = null, $name = null) - * @method \Cake\Mailer\Email addTo($email, $name = null) - * @method \Cake\Mailer\Email setCc($email, $name = null) + * @method \Cake\Mailer\Mailer returnPath($email = null, $name = null) + * @method \Cake\Mailer\Mailer addTo($email, $name = null) + * @method \Cake\Mailer\Mailer setCc($email, $name = null) * @method array getCc() - * @method \Cake\Mailer\Email cc($email = null, $name = null) - * @method \Cake\Mailer\Email addCc($email, $name = null) - * @method \Cake\Mailer\Email setBcc($email, $name = null) + * @method \Cake\Mailer\Mailer cc($email = null, $name = null) + * @method \Cake\Mailer\Mailer addCc($email, $name = null) + * @method \Cake\Mailer\Mailer setBcc($email, $name = null) * @method array getBcc() - * @method \Cake\Mailer\Email bcc($email = null, $name = null) - * @method \Cake\Mailer\Email addBcc($email, $name = null) - * @method \Cake\Mailer\Email setCharset($charset) + * @method \Cake\Mailer\Mailer bcc($email = null, $name = null) + * @method \Cake\Mailer\Mailer addBcc($email, $name = null) + * @method \Cake\Mailer\Mailer setCharset($charset) * @method string getCharset() - * @method \Cake\Mailer\Email charset($charset = null) - * @method \Cake\Mailer\Email setHeaderCharset($charset) + * @method \Cake\Mailer\Mailer charset($charset = null) + * @method \Cake\Mailer\Mailer setHeaderCharset($charset) * @method string getHeaderCharset() - * @method \Cake\Mailer\Email headerCharset($charset = null) - * @method \Cake\Mailer\Email setSubject($subject) + * @method \Cake\Mailer\Mailer headerCharset($charset = null) + * @method \Cake\Mailer\Mailer setSubject($subject) * @method string getSubject() - * @method \Cake\Mailer\Email subject($subject = null) - * @method \Cake\Mailer\Email setHeaders(array $headers) - * @method \Cake\Mailer\Email addHeaders(array $headers) - * @method \Cake\Mailer\Email getHeaders(array $include = []) - * @method \Cake\Mailer\Email setTemplate($template) + * @method \Cake\Mailer\Mailer subject($subject = null) + * @method \Cake\Mailer\Mailer setHeaders(array $headers) + * @method \Cake\Mailer\Mailer addHeaders(array $headers) + * @method \Cake\Mailer\Mailer getHeaders(array $include = []) + * @method \Cake\Mailer\Mailer setTemplate($template) * @method string getTemplate() - * @method \Cake\Mailer\Email setLayout($layout) + * @method \Cake\Mailer\Mailer setLayout($layout) * @method string getLayout() - * @method \Cake\Mailer\Email template($template = false, $layout = false) - * @method \Cake\Mailer\Email setViewRenderer($viewClass) + * @method \Cake\Mailer\Mailer template($template = false, $layout = false) + * @method \Cake\Mailer\Mailer setViewRenderer($viewClass) * @method string getViewRenderer() - * @method \Cake\Mailer\Email viewRender($viewClass = null) - * @method \Cake\Mailer\Email setViewVars($viewVars) + * @method \Cake\Mailer\Mailer viewRender($viewClass = null) + * @method \Cake\Mailer\Mailer setViewVars($viewVars) * @method array getViewVars() - * @method \Cake\Mailer\Email viewVars($viewVars = null) - * @method \Cake\Mailer\Email setTheme($theme) + * @method \Cake\Mailer\Mailer viewVars($viewVars = null) + * @method \Cake\Mailer\Mailer setTheme($theme) * @method string getTheme() - * @method \Cake\Mailer\Email theme($theme = null) - * @method \Cake\Mailer\Email setHelpers(array $helpers) + * @method \Cake\Mailer\Mailer theme($theme = null) + * @method \Cake\Mailer\Mailer setHelpers(array $helpers) * @method array getHelpers() - * @method \Cake\Mailer\Email helpers($helpers = null) - * @method \Cake\Mailer\Email setEmailFormat($format) + * @method \Cake\Mailer\Mailer helpers($helpers = null) + * @method \Cake\Mailer\Mailer setEmailFormat($format) * @method string getEmailFormat() - * @method \Cake\Mailer\Email emailFormat($format = null) - * @method \Cake\Mailer\Email setTransport($name) + * @method \Cake\Mailer\Mailer emailFormat($format = null) + * @method \Cake\Mailer\Mailer setTransport($name) * @method \Cake\Mailer\AbstractTransport getTransport() - * @method \Cake\Mailer\Email transport($name = null) - * @method \Cake\Mailer\Email setMessageId($message) + * @method \Cake\Mailer\Mailer transport($name = null) + * @method \Cake\Mailer\Mailer setMessageId($message) * @method bool|string getMessageId() - * @method \Cake\Mailer\Email messageId($message = null) - * @method \Cake\Mailer\Email setDomain($domain) + * @method \Cake\Mailer\Mailer messageId($message = null) + * @method \Cake\Mailer\Mailer setDomain($domain) * @method string getDomain() - * @method \Cake\Mailer\Email domain($domain = null) - * @method \Cake\Mailer\Email setAttachments($attachments) + * @method \Cake\Mailer\Mailer domain($domain = null) + * @method \Cake\Mailer\Mailer setAttachments($attachments) * @method array getAttachments() - * @method \Cake\Mailer\Email attachments($attachments = null) - * @method \Cake\Mailer\Email addAttachments($attachments) - * @method \Cake\Mailer\Email message($type = null) - * @method \Cake\Mailer\Email setProfile($config) + * @method \Cake\Mailer\Mailer attachments($attachments = null) + * @method \Cake\Mailer\Mailer addAttachments($attachments) + * @method \Cake\Mailer\Mailer message($type = null) + * @method \Cake\Mailer\Mailer setProfile($config) * @method string|array getProfile() - * @method \Cake\Mailer\Email profile($config = null) + * @method \Cake\Mailer\Mailer profile($config = null) + * @method \Cake\Mailer\Mailer setEmailPattern($regex) + * @method string getEmailPattern() + * @method \Cake\Mailer\Mailer emailPattern($regex = null) */ abstract class Mailer implements EventListenerInterface { @@ -166,7 +169,7 @@ abstract class Mailer implements EventListenerInterface * * @var string */ - static public $name; + public static $name; /** * Email instance. @@ -225,7 +228,9 @@ public function getName() */ public function layout($layout) { - deprecationWarning('Mailer::layout() is deprecated. Use setLayout() which sets the layout on the email class instead.'); + deprecationWarning( + 'Mailer::layout() is deprecated. Use $mailer->viewBuilder()->setLayout() instead.' + ); $this->_email->viewBuilder()->setLayout($layout); diff --git a/app/vendor/cakephp/cakephp/src/Mailer/Transport/SmtpTransport.php b/app/vendor/cakephp/cakephp/src/Mailer/Transport/SmtpTransport.php index ed1795530..835245059 100644 --- a/app/vendor/cakephp/cakephp/src/Mailer/Transport/SmtpTransport.php +++ b/app/vendor/cakephp/cakephp/src/Mailer/Transport/SmtpTransport.php @@ -78,6 +78,18 @@ public function __destruct() } } + /** + * Unserialize handler. + * + * Ensure that the socket property isn't reinitialized in a broken state. + * + * @return void + */ + public function __wakeup() + { + $this->_socket = null; + } + /** * Connect to the SMTP server. * @@ -178,7 +190,7 @@ public function send(Email $email) /** * Parses and stores the response lines in `'code' => 'message'` format. * - * @param array $responseLines Response lines to parse. + * @param string[] $responseLines Response lines to parse. * @return void */ protected function _bufferResponseLines(array $responseLines) diff --git a/app/vendor/cakephp/cakephp/src/Mailer/TransportFactory.php b/app/vendor/cakephp/cakephp/src/Mailer/TransportFactory.php new file mode 100644 index 000000000..77516151a --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Mailer/TransportFactory.php @@ -0,0 +1,114 @@ + 'Cake\Mailer\Transport\DebugTransport', + 'mail' => 'Cake\Mailer\Transport\MailTransport', + 'smtp' => 'Cake\Mailer\Transport\SmtpTransport', + ]; + + /** + * Returns the Transport Registry used for creating and using transport instances. + * + * @return \Cake\Mailer\TransportRegistry + */ + public static function getRegistry() + { + if (!static::$_registry) { + static::$_registry = new TransportRegistry(); + } + + return static::$_registry; + } + + /** + * Sets the Transport Registry instance used for creating and using transport instances. + * + * Also allows for injecting of a new registry instance. + * + * @param \Cake\Mailer\TransportRegistry $registry Injectable registry object. + * @return void + */ + public static function setRegistry(TransportRegistry $registry) + { + static::$_registry = $registry; + } + + /** + * Finds and builds the instance of the required tranport class. + * + * @param string $name Name of the config array that needs a tranport instance built + * @return void + * @throws \InvalidArgumentException When a tranport cannot be created. + */ + protected static function _buildTransport($name) + { + if (!isset(static::$_config[$name])) { + throw new InvalidArgumentException( + sprintf('The "%s" transport configuration does not exist', $name) + ); + } + + if (is_array(static::$_config[$name]) && empty(static::$_config[$name]['className'])) { + throw new InvalidArgumentException( + sprintf('Transport config "%s" is invalid, the required `className` option is missing', $name) + ); + } + + static::getRegistry()->load($name, static::$_config[$name]); + } + + /** + * Get transport instance. + * + * @param string $name Config name. + * @return \Cake\Mailer\AbstractTransport + */ + public static function get($name) + { + $registry = static::getRegistry(); + + if (isset($registry->{$name})) { + return $registry->{$name}; + } + + static::_buildTransport($name); + + return $registry->{$name}; + } +} diff --git a/app/vendor/cakephp/cakephp/src/Mailer/TransportRegistry.php b/app/vendor/cakephp/cakephp/src/Mailer/TransportRegistry.php new file mode 100644 index 000000000..3fba03b48 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Mailer/TransportRegistry.php @@ -0,0 +1,112 @@ +_loaded[$name]); + } +} diff --git a/app/vendor/cakephp/cakephp/src/Network/Socket.php b/app/vendor/cakephp/cakephp/src/Network/Socket.php index 82b79e282..9bc06e3fe 100644 --- a/app/vendor/cakephp/cakephp/src/Network/Socket.php +++ b/app/vendor/cakephp/cakephp/src/Network/Socket.php @@ -24,12 +24,9 @@ * CakePHP network socket connection class. * * Core base class for network communication. - * - * @mixin \Cake\Core\InstanceConfigTrait */ class Socket { - use InstanceConfigTrait; /** @@ -164,8 +161,13 @@ public function connect() } set_error_handler([$this, '_connectionErrorHandler']); + $remoteSocketTarget = $scheme . $this->_config['host']; + $port = (int)$this->_config['port']; + if ($port > 0) { + $remoteSocketTarget .= ':' . $port; + } $this->connection = stream_socket_client( - $scheme . $this->_config['host'] . ':' . $this->_config['port'], + $remoteSocketTarget, $errNum, $errStr, $this->_config['timeout'], diff --git a/app/vendor/cakephp/cakephp/src/ORM/Association.php b/app/vendor/cakephp/cakephp/src/ORM/Association.php index eda333f54..a408c8338 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Association.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Association.php @@ -105,14 +105,14 @@ abstract class Association /** * The field name in the owning side table that is used to match with the foreignKey * - * @var string|array + * @var string|string[] */ protected $_bindingKey; /** * The name of the field representing the foreign key to the table to load * - * @var string|array + * @var string|string[] */ protected $_foreignKey; @@ -179,8 +179,9 @@ abstract class Association /** * The default finder name to use for fetching rows from the target table + * With array value, finder name and default options are allowed. * - * @var string + * @var string|array */ protected $_finder = 'all'; @@ -334,14 +335,53 @@ public function cascadeCallbacks($cascadeCallbacks = null) return $this->getCascadeCallbacks(); } + /** + * Sets the class name of the target table object. + * + * @param string $className Class name to set. + * @return $this + * @throws \InvalidArgumentException In case the class name is set after the target table has been + * resolved, and it doesn't match the target table's class name. + */ + public function setClassName($className) + { + if ($this->_targetTable !== null && + get_class($this->_targetTable) !== App::className($className, 'Model/Table', 'Table') + ) { + throw new InvalidArgumentException( + 'The class name doesn\'t match the target table\'s class name.' + ); + } + + $this->_className = $className; + + return $this; + } + + /** + * Gets the class name of the target table object. + * + * @return string + */ + public function getClassName() + { + return $this->_className; + } + /** * The class name of the target table object * + * @deprecated 3.7.0 Use getClassName() instead. * @return string */ public function className() { - return $this->_className; + deprecationWarning( + get_called_class() . '::className() is deprecated. ' . + 'Use getClassName() instead.' + ); + + return $this->getClassName(); } /** @@ -521,7 +561,7 @@ public function conditions($conditions = null) * Sets the name of the field representing the binding field with the target table. * When not manually specified the primary key of the owning side table is used. * - * @param string|array $key the table field or fields to be used to link both tables together + * @param string|string[] $key the table field or fields to be used to link both tables together * @return $this */ public function setBindingKey($key) @@ -535,7 +575,7 @@ public function setBindingKey($key) * Gets the name of the field representing the binding field with the target table. * When not manually specified the primary key of the owning side table is used. * - * @return string|array + * @return string|string[] */ public function getBindingKey() { @@ -574,7 +614,7 @@ public function bindingKey($key = null) /** * Gets the name of the field representing the foreign key to the target table. * - * @return string|array + * @return string|string[] */ public function getForeignKey() { @@ -584,7 +624,7 @@ public function getForeignKey() /** * Sets the name of the field representing the foreign key to the target table. * - * @param string|array $key the key or keys to be used to link both tables together + * @param string|string[] $key the key or keys to be used to link both tables together * @return $this */ public function setForeignKey($key) @@ -859,7 +899,7 @@ public function strategy($name = null) /** * Gets the default finder to use for fetching rows from the target table. * - * @return string + * @return string|array */ public function getFinder() { @@ -869,7 +909,7 @@ public function getFinder() /** * Sets the default finder to use for fetching rows from the target table. * - * @param string $finder the finder name to use + * @param string|array $finder the finder name to use or array of finder name and option. * @return $this */ public function setFinder($finder) @@ -886,7 +926,7 @@ public function setFinder($finder) * * @deprecated 3.4.0 Use setFinder()/getFinder() instead. * @param string|null $finder the finder name to use - * @return string + * @return string|array */ public function finder($finder = null) { diff --git a/app/vendor/cakephp/cakephp/src/ORM/Association/BelongsToMany.php b/app/vendor/cakephp/cakephp/src/ORM/Association/BelongsToMany.php index 62e916e83..9020fd837 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Association/BelongsToMany.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Association/BelongsToMany.php @@ -105,7 +105,7 @@ class BelongsToMany extends Association /** * The name of the field representing the foreign key to the target table * - * @var string|array + * @var string|string[] */ protected $_targetForeignKey; @@ -160,7 +160,7 @@ class BelongsToMany extends Association /** * Sets the name of the field representing the foreign key to the target table. * - * @param string $key the key to be used to link both tables together + * @param string|string[] $key the key to be used to link both tables together * @return $this */ public function setTargetForeignKey($key) @@ -173,7 +173,7 @@ public function setTargetForeignKey($key) /** * Gets the name of the field representing the foreign key to the target table. * - * @return string + * @return string|string[] */ public function getTargetForeignKey() { @@ -809,7 +809,7 @@ protected function _saveTarget(EntityInterface $parentEntity, $entities, $option * * @param \Cake\Datasource\EntityInterface $sourceEntity the entity from source table in this * association - * @param array $targetEntities list of entities to link to link to the source entity using the + * @param \Cake\Datasource\EntityInterface[] $targetEntities list of entities to link to link to the source entity using the * junction table * @param array $options list of options accepted by `Table::save()` * @return bool success @@ -825,12 +825,12 @@ protected function _saveLinks(EntityInterface $sourceEntity, $targetEntities, $o $targetPrimaryKey = (array)$target->getPrimaryKey(); $bindingKey = (array)$this->getBindingKey(); $jointProperty = $this->_junctionProperty; - $junctionAlias = $junction->getAlias(); + $junctionRegistryAlias = $junction->getRegistryAlias(); foreach ($targetEntities as $e) { $joint = $e->get($jointProperty); if (!$joint || !($joint instanceof EntityInterface)) { - $joint = new $entityClass([], ['markNew' => true, 'source' => $junctionAlias]); + $joint = new $entityClass([], ['markNew' => true, 'source' => $junctionRegistryAlias]); } $sourceKeys = array_combine($foreignKey, $sourceEntity->extract($bindingKey)); $targetKeys = array_combine($assocForeignKey, $e->extract($targetPrimaryKey)); @@ -883,7 +883,7 @@ protected function _saveLinks(EntityInterface $sourceEntity, $targetEntities, $o * * @param \Cake\Datasource\EntityInterface $sourceEntity the row belonging to the `source` side * of this association - * @param array $targetEntities list of entities belonging to the `target` side + * @param \Cake\Datasource\EntityInterface[] $targetEntities list of entities belonging to the `target` side * of this association * @param array $options list of options to be passed to the internal `save` call * @throws \InvalidArgumentException when any of the values in $targetEntities is @@ -933,7 +933,7 @@ function () use ($sourceEntity, $targetEntities, $options) { * * @param \Cake\Datasource\EntityInterface $sourceEntity An entity persisted in the source table for * this association. - * @param array $targetEntities List of entities persisted in the target table for + * @param \Cake\Datasource\EntityInterface[] $targetEntities List of entities persisted in the target table for * this association. * @param array|bool $options List of options to be passed to the internal `delete` call, * or a `boolean` as `cleanProperty` key shortcut. @@ -1130,7 +1130,7 @@ protected function _appendJunctionJoin($query, $conditions) { $name = $this->_junctionAssociationName(); /** @var array $joins */ - $joins = $query->join(); + $joins = $query->clause('join'); $matching = [ $name => [ 'table' => $this->junction()->getTable(), @@ -1251,7 +1251,7 @@ function () use ($sourceEntity, $targetEntities, $primaryValue, $options) { * `$targetEntities` that were not deleted from calculating the difference. * * @param \Cake\ORM\Query $existing a query for getting existing links - * @param array $jointEntities link entities that should be persisted + * @param \Cake\Datasource\EntityInterface[] $jointEntities link entities that should be persisted * @param array $targetEntities entities in target table that are related to * the `$jointEntities` * @param array $options list of options accepted by `Table::delete()` @@ -1318,7 +1318,7 @@ protected function _diffLinks($existing, $jointEntities, $targetEntities, $optio * * @param \Cake\Datasource\EntityInterface $sourceEntity the row belonging to the `source` side * of this association - * @param array $targetEntities list of entities belonging to the `target` side + * @param \Cake\Datasource\EntityInterface[] $targetEntities list of entities belonging to the `target` side * of this association * @return bool * @throws \InvalidArgumentException @@ -1350,7 +1350,7 @@ protected function _checkPersistenceStatus($sourceEntity, array $targetEntities) * association. * @throws \InvalidArgumentException if any of the entities is lacking a primary * key value - * @return array + * @return \Cake\Datasource\EntityInterface[] */ protected function _collectJointEntities($sourceEntity, $targetEntities) { diff --git a/app/vendor/cakephp/cakephp/src/ORM/Association/HasMany.php b/app/vendor/cakephp/cakephp/src/ORM/Association/HasMany.php index 84999d029..3d39cbe74 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Association/HasMany.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Association/HasMany.php @@ -110,7 +110,7 @@ public function isOwningSide(Table $side) */ public function setSaveStrategy($strategy) { - if (!in_array($strategy, [self::SAVE_APPEND, self::SAVE_REPLACE])) { + if (!in_array($strategy, [self::SAVE_APPEND, self::SAVE_REPLACE], true)) { $msg = sprintf('Invalid save strategy "%s"', $strategy); throw new InvalidArgumentException($msg); } @@ -374,6 +374,7 @@ public function unlink(EntityInterface $sourceEntity, array $targetEntities, $op $conditions = [ 'OR' => (new Collection($targetEntities)) ->map(function ($entity) use ($targetPrimaryKey) { + /** @var \Cake\Datasource\EntityInterface $entity */ return $entity->extract($targetPrimaryKey); }) ->toList() @@ -476,6 +477,7 @@ protected function _unlinkAssociated(array $foreignKeyReference, EntityInterface $exclusions = new Collection($remainingEntities); $exclusions = $exclusions->map( function ($ent) use ($primaryKey) { + /** @var \Cake\Datasource\EntityInterface $ent */ return $ent->extract($primaryKey); } ) @@ -484,7 +486,7 @@ function ($v) { return !in_array(null, $v, true); } ) - ->toArray(); + ->toList(); $conditions = $foreignKeyReference; diff --git a/app/vendor/cakephp/cakephp/src/ORM/Association/Loader/SelectLoader.php b/app/vendor/cakephp/cakephp/src/ORM/Association/Loader/SelectLoader.php index 2c01816da..14e896370 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Association/Loader/SelectLoader.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Association/Loader/SelectLoader.php @@ -276,7 +276,7 @@ protected function _assertFieldsPresent($fetchQuery, $key) * filtering needs to be done using a subquery. * * @param \Cake\ORM\Query $query Target table's query - * @param string|array $key the fields that should be used for filtering + * @param string|string[] $key the fields that should be used for filtering * @param \Cake\ORM\Query $subquery The Subquery to use for filtering * @return \Cake\ORM\Query */ @@ -334,7 +334,7 @@ protected function _addFilteringCondition($query, $key, $filter) * from $keys with the tuple values in $filter using the provided operator. * * @param \Cake\ORM\Query $query Target table's query - * @param array $keys the fields that should be used for filtering + * @param string[] $keys the fields that should be used for filtering * @param mixed $filter the value that should be used to match for $key * @param string $operator The operator for comparing the tuples * @return \Cake\Database\Expression\TupleComparison @@ -357,7 +357,8 @@ protected function _createTupleCondition($query, $keys, $filter, $operator) * which the filter should be applied * * @param array $options The options for getting the link field. - * @return string|array + * @return string|string[] + * @throws \RuntimeException */ protected function _linkField($options) { @@ -396,7 +397,7 @@ protected function _linkField($options) protected function _buildSubquery($query) { $filterQuery = clone $query; - $filterQuery->enableAutoFields(false); + $filterQuery->disableAutoFields(); $filterQuery->mapReduce(null, null, true); $filterQuery->formatResults(null, true); $filterQuery->contain([], true); @@ -459,8 +460,8 @@ protected function _subqueryFields($query) protected function _buildResultMap($fetchQuery, $options) { $resultMap = []; - $singleResult = in_array($this->associationType, [Association::MANY_TO_ONE, Association::ONE_TO_ONE]); - $keys = in_array($this->associationType, [Association::ONE_TO_ONE, Association::ONE_TO_MANY]) ? + $singleResult = in_array($this->associationType, [Association::MANY_TO_ONE, Association::ONE_TO_ONE], true); + $keys = in_array($this->associationType, [Association::ONE_TO_ONE, Association::ONE_TO_MANY], true) ? $this->foreignKey : $this->bindingKey; $key = (array)$keys; @@ -524,7 +525,7 @@ protected function _resultInjector($fetchQuery, $resultMap, $options) * be done with multiple foreign keys * * @param array $resultMap A keyed arrays containing the target table - * @param array $sourceKeys An array with aliased keys to match + * @param string[] $sourceKeys An array with aliased keys to match * @param string $nestKey The key under which results should be nested * @return \Closure */ diff --git a/app/vendor/cakephp/cakephp/src/ORM/Association/Loader/SelectWithPivotLoader.php b/app/vendor/cakephp/cakephp/src/ORM/Association/Loader/SelectWithPivotLoader.php index 733a8c7fa..521a2ace5 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Association/Loader/SelectWithPivotLoader.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Association/Loader/SelectWithPivotLoader.php @@ -133,7 +133,7 @@ protected function _buildQuery($options) * which the filter should be applied * * @param array $options the options to use for getting the link field. - * @return array|string + * @return string|string[] */ protected function _linkField($options) { diff --git a/app/vendor/cakephp/cakephp/src/ORM/AssociationCollection.php b/app/vendor/cakephp/cakephp/src/ORM/AssociationCollection.php index 574648f0d..a6ccca210 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/AssociationCollection.php +++ b/app/vendor/cakephp/cakephp/src/ORM/AssociationCollection.php @@ -143,7 +143,7 @@ public function has($alias) /** * Get the names of all the associations in the collection. * - * @return array + * @return string[] */ public function keys() { diff --git a/app/vendor/cakephp/cakephp/src/ORM/Behavior.php b/app/vendor/cakephp/cakephp/src/ORM/Behavior.php index ab11b6749..fce15777b 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Behavior.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Behavior.php @@ -109,7 +109,6 @@ * * @see \Cake\ORM\Table::addBehavior() * @see \Cake\Event\EventManager - * @mixin \Cake\Core\InstanceConfigTrait */ class Behavior implements EventListenerInterface { @@ -404,7 +403,7 @@ protected function _reflectionCache() foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { $methodName = $method->getName(); - if (in_array($methodName, $baseMethods) || + if (in_array($methodName, $baseMethods, true) || isset($eventMethods[$methodName]) ) { continue; diff --git a/app/vendor/cakephp/cakephp/src/ORM/Behavior/TranslateBehavior.php b/app/vendor/cakephp/cakephp/src/ORM/Behavior/TranslateBehavior.php index 77f9dcce4..9d3219ad2 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Behavior/TranslateBehavior.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Behavior/TranslateBehavior.php @@ -334,7 +334,7 @@ public function beforeSave(Event $event, EntityInterface $entity, ArrayObject $o 'foreign_key' => $key, 'model' => $model ]) - ->enableBufferedResults(false) + ->disableBufferedResults() ->all() ->indexBy('field'); @@ -752,8 +752,8 @@ protected function _findExistingTranslations($ruleSet) $query = $association->find() ->select(['id', 'num' => 0]) ->where(current($ruleSet)) - ->enableHydration(false) - ->enableBufferedResults(false); + ->disableHydration() + ->disableBufferedResults(); unset($ruleSet[0]); foreach ($ruleSet as $i => $conditions) { diff --git a/app/vendor/cakephp/cakephp/src/ORM/Behavior/TreeBehavior.php b/app/vendor/cakephp/cakephp/src/ORM/Behavior/TreeBehavior.php index 70714be2e..44b32daac 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Behavior/TreeBehavior.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Behavior/TreeBehavior.php @@ -837,7 +837,7 @@ protected function _recoverTree($counter = 0, $parentId = null, $level = -1) ->select([$aliasedPrimaryKey]) ->where([$this->_table->aliasField($parent) . ' IS' => $parentId]) ->order($order) - ->enableHydration(false); + ->disableHydration(); $leftCounter = $counter; $nextLevel = $level + 1; diff --git a/app/vendor/cakephp/cakephp/src/ORM/EagerLoader.php b/app/vendor/cakephp/cakephp/src/ORM/EagerLoader.php index 749f43665..83f0d4bf7 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/EagerLoader.php +++ b/app/vendor/cakephp/cakephp/src/ORM/EagerLoader.php @@ -209,6 +209,18 @@ public function enableAutoFields($enable = true) return $this; } + /** + * Disable auto loading fields of contained associations. + * + * @return $this + */ + public function disableAutoFields() + { + $this->_autoFields = false; + + return $this; + } + /** * Gets whether or not contained associations will load fields automatically. * @@ -545,7 +557,14 @@ protected function _normalizeContain(Table $parent, $alias, $options, $paths) $paths += ['aliasPath' => '', 'propertyPath' => '', 'root' => $alias]; $paths['aliasPath'] .= '.' . $alias; - $paths['propertyPath'] .= '.' . $instance->getProperty(); + + if (isset($options['matching']) && + $options['matching'] === true + ) { + $paths['propertyPath'] = '_matchingData.' . $alias; + } else { + $paths['propertyPath'] .= '.' . $instance->getProperty(); + } $table = $instance->getTarget(); diff --git a/app/vendor/cakephp/cakephp/src/ORM/Exception/PersistenceFailedException.php b/app/vendor/cakephp/cakephp/src/ORM/Exception/PersistenceFailedException.php index 4bfb1db00..8596f1e98 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Exception/PersistenceFailedException.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Exception/PersistenceFailedException.php @@ -14,6 +14,7 @@ use Cake\Core\Exception\Exception; use Cake\Datasource\EntityInterface; +use Cake\Utility\Hash; /** * Used when a strict save or delete fails @@ -47,35 +48,17 @@ public function __construct(EntityInterface $entity, $message, $code = null, $pr $this->_entity = $entity; if (is_array($message)) { $errors = []; - foreach ($entity->getErrors() as $field => $error) { - $errors[] = $field . ': "' . $this->buildError($error) . '"'; + foreach (Hash::flatten($entity->getErrors()) as $field => $error) { + $errors[] = $field . ': "' . $error . '"'; } if ($errors) { $message[] = implode(', ', $errors); - $this->_messageTemplate = 'Entity %s failure (%s).'; + $this->_messageTemplate = 'Entity %s failure. Found the following errors (%s).'; } } parent::__construct($message, $code, $previous); } - /** - * @param string|array $error Error message. - * @return string - */ - protected function buildError($error) - { - if (!is_array($error)) { - return $error; - } - - $errors = []; - foreach ($error as $key => $message) { - $errors[] = is_int($key) ? $message : $key; - } - - return implode(', ', $errors); - } - /** * Get the passed in entity * diff --git a/app/vendor/cakephp/cakephp/src/ORM/LazyEagerLoader.php b/app/vendor/cakephp/cakephp/src/ORM/LazyEagerLoader.php index 092a6534d..0fedcffcc 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/LazyEagerLoader.php +++ b/app/vendor/cakephp/cakephp/src/ORM/LazyEagerLoader.php @@ -86,14 +86,17 @@ protected function _getQuery($objects, $contain, $source) } if (is_string($primaryKey)) { + /** @var \Cake\Database\Expression\QueryExpression $exp */ return $exp->in($source->aliasField($primaryKey), $keys->toList()); } + /** @var \Cake\ORM\Query $q */ $types = array_intersect_key($q->getDefaultTypes(), array_flip($primaryKey)); $primaryKey = array_map([$source, 'aliasField'], $primaryKey); return new TupleComparison($primaryKey, $keys->toList(), $types, 'IN'); }) + ->enableAutoFields() ->contain($contain); foreach ($query->getEagerLoader()->attachableAssociations($source) as $loadable) { @@ -110,7 +113,7 @@ protected function _getQuery($objects, $contain, $source) * in the top level entities. * * @param \Cake\ORM\Table $source The table having the top level associations - * @param array $associations The name of the top level associations + * @param string[] $associations The name of the top level associations * @return array */ protected function _getPropertyMap($source, $associations) @@ -128,9 +131,9 @@ protected function _getPropertyMap($source, $associations) * Injects the results of the eager loader query into the original list of * entities. * - * @param array|\Traversable $objects The original list of entities + * @param \Cake\Datasource\EntityInterface[]|\Traversable $objects The original list of entities * @param \Cake\Collection\CollectionInterface|\Cake\Database\Query $results The loaded results - * @param array $associations The top level associations that were loaded + * @param string[] $associations The top level associations that were loaded * @param \Cake\ORM\Table $source The table where the entities came from * @return array */ @@ -141,6 +144,7 @@ protected function _injectResults($objects, $results, $associations, $source) $primaryKey = (array)$source->getPrimaryKey(); $results = $results ->indexBy(function ($e) use ($primaryKey) { + /** @var \Cake\Datasource\EntityInterface $e */ return implode(';', $e->extract($primaryKey)); }) ->toArray(); @@ -152,6 +156,7 @@ protected function _injectResults($objects, $results, $associations, $source) continue; } + /** @var \Cake\Datasource\EntityInterface $loaded */ $loaded = $results[$key]; foreach ($associations as $assoc) { $property = $properties[$assoc]; diff --git a/app/vendor/cakephp/cakephp/src/ORM/Locator/TableLocator.php b/app/vendor/cakephp/cakephp/src/ORM/Locator/TableLocator.php index 7597cef8e..215d1f6e9 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Locator/TableLocator.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Locator/TableLocator.php @@ -27,6 +27,13 @@ class TableLocator implements LocatorInterface { + /** + * Contains a list of locations where table classes should be looked for. + * + * @var array + */ + protected $locations = []; + /** * Configuration for aliases. * @@ -56,6 +63,25 @@ class TableLocator implements LocatorInterface */ protected $_options = []; + /** + * Constructor. + * + * @param array|null $locations Locations where tables should be looked for. + * If none provided, the default `Model\Table` under your app's namespace is used. + */ + public function __construct(array $locations = null) + { + if ($locations === null) { + $locations = [ + 'Model/Table', + ]; + } + + foreach ($locations as $location) { + $this->addLocation($location); + } + } + /** * Stores a list of options to be used when instantiating an object * with a matching alias. @@ -243,7 +269,18 @@ protected function _getClassName($alias, array $options = []) $options['className'] = Inflector::camelize($alias); } - return App::className($options['className'], 'Model/Table', 'Table'); + if (strpos($options['className'], '\\') !== false && class_exists($options['className'])) { + return $options['className']; + } + + foreach ($this->locations as $location) { + $class = App::className($options['className'], $location, 'Table'); + if ($class !== false) { + return $class; + } + } + + return false; } /** @@ -307,4 +344,20 @@ public function remove($alias) $this->_fallbacked[$alias] ); } + + /** + * Adds a location where tables should be looked for. + * + * @param string $location Location to add. + * @return $this + * + * @since 3.8.0 + */ + public function addLocation($location) + { + $location = str_replace('\\', '/', $location); + $this->locations[] = trim($location, '/'); + + return $this; + } } diff --git a/app/vendor/cakephp/cakephp/src/ORM/Marshaller.php b/app/vendor/cakephp/cakephp/src/ORM/Marshaller.php index a38fecb41..a54160d86 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Marshaller.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Marshaller.php @@ -309,7 +309,7 @@ protected function _marshalAssociation($assoc, $value, $options) $targetTable = $assoc->getTarget(); $marshaller = $targetTable->marshaller(); $types = [Association::ONE_TO_ONE, Association::MANY_TO_ONE]; - if (in_array($assoc->type(), $types)) { + if (in_array($assoc->type(), $types, true)) { return $marshaller->one($value, (array)$options); } if ($assoc->type() === Association::ONE_TO_MANY || $assoc->type() === Association::MANY_TO_MANY) { @@ -747,13 +747,24 @@ protected function _mergeAssociation($original, $assoc, $value, $options) $targetTable = $assoc->getTarget(); $marshaller = $targetTable->marshaller(); $types = [Association::ONE_TO_ONE, Association::MANY_TO_ONE]; - if (in_array($assoc->type(), $types)) { + if (in_array($assoc->type(), $types, true)) { return $marshaller->merge($original, $value, (array)$options); } if ($assoc->type() === Association::MANY_TO_MANY) { return $marshaller->_mergeBelongsToMany($original, $assoc, $value, (array)$options); } + if ($assoc->type() === Association::ONE_TO_MANY) { + $hasIds = array_key_exists('_ids', $value); + $onlyIds = array_key_exists('onlyIds', $options) && $options['onlyIds']; + if ($hasIds && is_array($value['_ids'])) { + return $this->_loadAssociatedByIds($assoc, $value['_ids']); + } + if ($hasIds || $onlyIds) { + return []; + } + } + return $marshaller->mergeMany($original, $value, (array)$options); } @@ -781,7 +792,7 @@ protected function _mergeBelongsToMany($original, $assoc, $value, $options) return []; } - if (!empty($associated) && !in_array('_joinData', $associated) && !isset($associated['_joinData'])) { + if (!empty($associated) && !in_array('_joinData', $associated, true) && !isset($associated['_joinData'])) { return $this->mergeMany($original, $value, $options); } diff --git a/app/vendor/cakephp/cakephp/src/ORM/Query.php b/app/vendor/cakephp/cakephp/src/ORM/Query.php index 1f52d831d..4107290fd 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Query.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Query.php @@ -45,7 +45,7 @@ * @method mixed min($field, $type = SORT_NUMERIC) Returns the minimum value for a single column in all the results. * @method \Cake\Collection\CollectionInterface groupBy(string|callable $field) In-memory group all results by the value of a column. * @method \Cake\Collection\CollectionInterface indexBy(string|callable $field) Returns the results indexed by the value of a column. - * @method int countBy(string|callable $field) Returns the number of unique values for a column + * @method \Cake\Collection\CollectionInterface countBy(string|callable $field) Returns the number of unique values for a column * @method float sumOf(string|callable $field) Returns the sum of all values for a single column * @method \Cake\Collection\CollectionInterface shuffle() In-memory randomize the order the results are returned * @method \Cake\Collection\CollectionInterface sample($size = 10) In-memory shuffle the results and return a subset of them. @@ -66,11 +66,9 @@ * with the first rows of the query and each of the items, then the second rows and so on. * @method \Cake\Collection\CollectionInterface chunk($size) Groups the results in arrays of $size rows each. * @method bool isEmpty() Returns true if this query found no results. - * @mixin \Cake\Datasource\QueryTrait */ class Query extends DatabaseQuery implements JsonSerializable, QueryInterface { - use QueryTrait { cache as private _cache; all as private _all; @@ -233,7 +231,7 @@ public function select($fields = [], $overwrite = false) * * * @param \Cake\ORM\Table|\Cake\ORM\Association $table The table to use to get an array of columns - * @param array $excludedFields The un-aliased column names you do not want selected from $table + * @param string[] $excludedFields The un-aliased column names you do not want selected from $table * @param bool $overwrite Whether to reset/remove previous selected fields * @return Query * @throws \InvalidArgumentException If Association|Table is not passed in first argument @@ -1010,6 +1008,22 @@ public function enableHydration($enable = true) return $this; } + /** + * Disable hydrating entities. + * + * Disabling hydration will cause array results to be returned for the query + * instead of entities. + * + * @return $this + */ + public function disableHydration() + { + $this->_dirty(); + $this->_hydrate = false; + + return $this; + } + /** * Returns the current hydration mode. * @@ -1269,6 +1283,7 @@ public function delete($table = null) $repository = $this->getRepository(); $this->from([$repository->getAlias() => $repository->getTable()]); + // We do not pass $table to parent class here return parent::delete(); } @@ -1358,6 +1373,18 @@ public function enableAutoFields($value = true) return $this; } + /** + * Disables automatically appending fields. + * + * @return $this + */ + public function disableAutoFields() + { + $this->_autoFields = false; + + return $this; + } + /** * Gets whether or not the ORM should automatically append fields. * diff --git a/app/vendor/cakephp/cakephp/src/ORM/ResultSet.php b/app/vendor/cakephp/cakephp/src/ORM/ResultSet.php index 18d4a3627..5d1554311 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/ResultSet.php +++ b/app/vendor/cakephp/cakephp/src/ORM/ResultSet.php @@ -65,7 +65,7 @@ class ResultSet implements ResultSetInterface /** * Default table instance * - * @var \Cake\ORM\Table + * @var \Cake\ORM\Table|\Cake\Datasource\RepositoryInterface */ protected $_defaultTable; @@ -155,6 +155,7 @@ class ResultSet implements ResultSetInterface * Converters are indexed by alias and column name. * * @var array + * @deprecated 3.2.0 Not used anymore. Type casting is done at the statement level */ protected $_types = []; @@ -443,6 +444,7 @@ protected function _calculateTypeMap() * @param \Cake\ORM\Table $table The table from which to get the schema * @param array $fields The fields whitelist to use for fields in the schema. * @return array + * @deprecated 3.2.0 Not used anymore. Type casting is done at the statement level */ protected function _getTypes($table, $fields) { @@ -528,6 +530,13 @@ protected function _groupResult($row) $presentAliases[$table] = true; } + // If the default table is not in the results, set + // it to an empty array so that any contained + // associations hydrate correctly. + if (!isset($results[$defaultAlias])) { + $results[$defaultAlias] = []; + } + unset($presentAliases[$defaultAlias]); foreach ($this->_containMap as $assoc) { diff --git a/app/vendor/cakephp/cakephp/src/ORM/Rule/IsUnique.php b/app/vendor/cakephp/cakephp/src/ORM/Rule/IsUnique.php index 2d024ed16..53713b1be 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Rule/IsUnique.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Rule/IsUnique.php @@ -25,7 +25,7 @@ class IsUnique /** * The list of fields to check * - * @var array + * @var string[] */ protected $_fields; @@ -45,7 +45,7 @@ class IsUnique * multi-column unique rules. By default this is `true` to emulate how SQL UNIQUE * keys work. * - * @param array $fields The list of fields to check uniqueness for + * @param string[] $fields The list of fields to check uniqueness for * @param array $options The additional options for this rule. */ public function __construct(array $fields, array $options = []) diff --git a/app/vendor/cakephp/cakephp/src/ORM/RulesChecker.php b/app/vendor/cakephp/cakephp/src/ORM/RulesChecker.php index feea8c898..c9e1d8a99 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/RulesChecker.php +++ b/app/vendor/cakephp/cakephp/src/ORM/RulesChecker.php @@ -39,7 +39,7 @@ class RulesChecker extends BaseRulesChecker * $rules->add($rules->isUnique(['email'], 'The email should be unique')); * ``` * - * @param array $fields The list of fields to check for uniqueness. + * @param string[] $fields The list of fields to check for uniqueness. * @param string|array|null $message The error message to show in case the rule does not pass. Can * also be an array of options. When an array, the 'message' key can be used to provide a message. * @return callable diff --git a/app/vendor/cakephp/cakephp/src/ORM/Table.php b/app/vendor/cakephp/cakephp/src/ORM/Table.php index 065e635d1..4084a3dc2 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Table.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Table.php @@ -150,7 +150,14 @@ class Table implements RepositoryInterface, EventListenerInterface, EventDispatc * * @var string */ - const RULES_CLASS = 'Cake\ORM\RulesChecker'; + const RULES_CLASS = RulesChecker::class; + + /** + * The IsUnique class name that is used. + * + * @var string + */ + const IS_UNIQUE_CLASS = IsUnique::class; /** * Name of the table as it can be found in the database @@ -184,7 +191,7 @@ class Table implements RepositoryInterface, EventListenerInterface, EventDispatc /** * The name of the field that represents the primary key in the table * - * @var string|array + * @var string|string[] */ protected $_primaryKey; @@ -332,6 +339,9 @@ public function initialize(array $config) /** * Sets the database table name. * + * This can include the database schema name in the form 'schema.table'. + * If the name must be quoted, enable automatic identifier quoting. + * * @param string $table Table name. * @return $this */ @@ -345,6 +355,8 @@ public function setTable($table) /** * Returns the database table name. * + * This can include the database schema name if set using `setTable()`. + * * @return string */ public function getTable() @@ -494,7 +506,7 @@ public function registryAlias($registryAlias = null) /** * Sets the connection instance. * - * @param \Cake\Database\Connection|\Cake\Datasource\ConnectionInterface $connection The connection instance + * @param \Cake\Database\Connection $connection The connection instance * @return $this */ public function setConnection(ConnectionInterface $connection) @@ -652,7 +664,7 @@ public function hasField($field) /** * Sets the primary key field name. * - * @param string|array $key Sets a new name to be used as primary key + * @param string|string[] $key Sets a new name to be used as primary key * @return $this */ public function setPrimaryKey($key) @@ -665,7 +677,7 @@ public function setPrimaryKey($key) /** * Returns the primary key field name. * - * @return string|array + * @return string|string[] */ public function getPrimaryKey() { @@ -684,8 +696,8 @@ public function getPrimaryKey() * Returns the primary key field name or sets a new one * * @deprecated 3.4.0 Use setPrimaryKey()/getPrimaryKey() instead. - * @param string|array|null $key Sets a new name to be used as primary key - * @return string|array + * @param string|string[]|null $key Sets a new name to be used as primary key + * @return string|string[] */ public function primaryKey($key = null) { @@ -1586,7 +1598,7 @@ public function get($primaryKey, $options = []) throw new InvalidPrimaryKeyException(sprintf( 'Record not found in table "%s" with primary key [%s]', $this->getTable(), - implode($primaryKey, ', ') + implode(', ', $primaryKey) )); } $conditions = array_combine($key, $primaryKey); @@ -1667,7 +1679,7 @@ protected function _transactionCommitted($atomic, $primary) * transaction (default: true) * - defaults: Whether to use the search criteria as default values for the new entity (default: true) * - * @param array|\Cake\ORM\Query $search The criteria to find existing + * @param array|callable|\Cake\ORM\Query $search The criteria to find existing * records by. Note that when you pass a query object you'll have to use * the 2nd arg of the method to modify the entity data before saving. * @param callable|null $callback A callback that will be invoked for newly @@ -1675,6 +1687,7 @@ protected function _transactionCommitted($atomic, $primary) * is persisted. * @param array $options The options to use when saving. * @return \Cake\Datasource\EntityInterface An entity. + * @throws \Cake\ORM\Exception\PersistenceFailedException When the entity couldn't be saved */ public function findOrCreate($search, callable $callback = null, $options = []) { @@ -1697,55 +1710,62 @@ public function findOrCreate($search, callable $callback = null, $options = []) /** * Performs the actual find and/or create of an entity based on the passed options. * - * @param array|callable $search The criteria to find an existing record by, or a callable tha will + * @param array|callable|\Cake\ORM\Query $search The criteria to find an existing record by, or a callable tha will * customize the find query. * @param callable|null $callback A callback that will be invoked for newly * created entities. This callback will be called *before* the entity * is persisted. * @param array $options The options to use when saving. * @return \Cake\Datasource\EntityInterface An entity. + * @throws \Cake\ORM\Exception\PersistenceFailedException When the entity couldn't be saved */ protected function _processFindOrCreate($search, callable $callback = null, $options = []) { - if (is_callable($search)) { - $query = $this->find(); - $search($query); - } elseif (is_array($search)) { - $query = $this->find()->where($search); - } elseif ($search instanceof Query) { - $query = $search; - } else { - throw new InvalidArgumentException('Search criteria must be an array, callable or Query'); - } + $query = $this->_getFindOrCreateQuery($search); $row = $query->first(); if ($row !== null) { return $row; } + $entity = $this->newEntity(); if ($options['defaults'] && is_array($search)) { - $entity->set($search, ['guard' => false]); + $accessibleFields = array_combine(array_keys($search), array_fill(0, count($search), true)); + $entity = $this->patchEntity($entity, $search, ['accessibleFields' => $accessibleFields]); } if ($callback !== null) { $entity = $callback($entity) ?: $entity; } unset($options['defaults']); - return $this->save($entity, $options) ?: $entity; + $result = $this->save($entity, $options); + + if ($result === false) { + throw new PersistenceFailedException($entity, ['findOrCreate']); + } + + return $entity; } /** * Gets the query object for findOrCreate(). * - * @param array|\Cake\ORM\Query|string $search The criteria to find existing records by. + * @param array|callable|\Cake\ORM\Query $search The criteria to find existing records by. * @return \Cake\ORM\Query */ protected function _getFindOrCreateQuery($search) { - if ($search instanceof Query) { - return $search; + if (is_callable($search)) { + $query = $this->find(); + $search($query); + } elseif (is_array($search)) { + $query = $this->find()->where($search); + } elseif ($search instanceof Query) { + $query = $search; + } else { + throw new InvalidArgumentException('Search criteria must be an array, callable or Query'); } - return $this->find()->where($search); + return $query; } /** @@ -1797,7 +1817,7 @@ public function exists($conditions) ->select(['existing' => 1]) ->where($conditions) ->limit(1) - ->enableHydration(false) + ->disableHydration() ->toArray() ); } @@ -1902,7 +1922,7 @@ public function save(EntityInterface $entity, $options = []) '_primary' => true ]); - if ($entity->getErrors()) { + if ($entity->hasErrors($options['associated'])) { return false; } @@ -2079,7 +2099,9 @@ protected function _insert($entity, $data) $primary = array_combine($primary, $id); $primary = array_intersect_key($data, $primary) + $primary; - $filteredKeys = array_filter($primary, 'strlen'); + $filteredKeys = array_filter($primary, function ($v) { + return $v !== null; + }); $data += $filteredKeys; if (count($primary) > 1) { @@ -2135,8 +2157,8 @@ protected function _insert($entity, $data) * Note: The ORM will not generate primary key values for composite primary keys. * You can overwrite _newId() in your table class. * - * @param array $primary The primary key columns to get a new ID for. - * @return null|string|array Either null or the primary key value or a list of primary key values. + * @param string[] $primary The primary key columns to get a new ID for. + * @return string|null Either null or the primary key value or a list of primary key values. */ protected function _newId($primary) { @@ -2202,9 +2224,10 @@ protected function _update($entity, $data) * any one of the records fails to save due to failed validation or database * error. * - * @param \Cake\Datasource\EntityInterface[]|\Cake\ORM\ResultSet $entities Entities to save. + * @param \Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface $entities Entities to save. * @param array|\ArrayAccess $options Options used when calling Table::save() for each entity. - * @return bool|\Cake\Datasource\EntityInterface[]|\Cake\ORM\ResultSet False on failure, entities list on success. + * @return bool|\Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface False on failure, entities list on success. + * @throws \Exception */ public function saveMany($entities, $options = []) { @@ -2517,9 +2540,11 @@ public function __get($property) $association = $this->_associations->get($property); if (!$association) { throw new RuntimeException(sprintf( - 'Table "%s" is not associated with "%s"', - get_class($this), - $property + 'Undefined property `%s`. ' . + 'You have not defined the `%s` association on `%s`.', + $property, + $property, + static::class )); } @@ -2797,7 +2822,8 @@ public function validateUnique($value, array $options, array $context = null) return false; } } - $rule = new IsUnique($fields, $options); + $class = static::IS_UNIQUE_CLASS; + $rule = new $class($fields, $options); return $rule($entity, ['repository' => $this]); } diff --git a/app/vendor/cakephp/cakephp/src/ORM/TableRegistry.php b/app/vendor/cakephp/cakephp/src/ORM/TableRegistry.php index 5c952687a..ee5056886 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/TableRegistry.php +++ b/app/vendor/cakephp/cakephp/src/ORM/TableRegistry.php @@ -24,12 +24,15 @@ * * ### Configuring instances * - * You may need to configure your table objects, using TableRegistry you can + * You may need to configure your table objects. Using the `TableLocator` you can * centralize configuration. Any configuration set before instances are created * will be used when creating instances. If you modify configuration after * an instance is made, the instances *will not* be updated. * * ``` + * TableRegistry::getTableLocator()->setConfig('Users', ['table' => 'my_users']); + * + * // Prior to 3.6.0 * TableRegistry::config('Users', ['table' => 'my_users']); * ``` * @@ -38,12 +41,15 @@ * * ### Getting instances * - * You can fetch instances out of the registry using get(). One instance is stored - * per alias. Once an alias is populated the same instance will always be returned. - * This is used to make the ORM use less memory and help make cyclic references easier - * to solve. + * You can fetch instances out of the registry through `TableLocator::get()`. + * One instance is stored per alias. Once an alias is populated the same + * instance will always be returned. This reduces the ORM memory cost and + * helps make cyclic references easier to solve. * * ``` + * $table = TableRegistry::getTableLocator()->get('Users', $config); + * + * // Prior to 3.6.0 * $table = TableRegistry::get('Users', $config); * ``` */ diff --git a/app/vendor/cakephp/cakephp/src/Routing/DispatcherFilter.php b/app/vendor/cakephp/cakephp/src/Routing/DispatcherFilter.php index 56ba8daf0..3cb186d48 100644 --- a/app/vendor/cakephp/cakephp/src/Routing/DispatcherFilter.php +++ b/app/vendor/cakephp/cakephp/src/Routing/DispatcherFilter.php @@ -62,12 +62,9 @@ * * When using the `for` or `when` matchers, conditions will be re-checked on the before and after * callback as the conditions could change during the dispatch cycle. - * - * @mixin \Cake\Core\InstanceConfigTrait */ class DispatcherFilter implements EventListenerInterface { - use InstanceConfigTrait; /** diff --git a/app/vendor/cakephp/cakephp/src/Routing/Filter/AssetFilter.php b/app/vendor/cakephp/cakephp/src/Routing/Filter/AssetFilter.php index e96ab526f..b60b045fc 100644 --- a/app/vendor/cakephp/cakephp/src/Routing/Filter/AssetFilter.php +++ b/app/vendor/cakephp/cakephp/src/Routing/Filter/AssetFilter.php @@ -109,7 +109,7 @@ protected function _getAssetFile($url) } $pluginPart[] = Inflector::camelize($parts[$i]); $plugin = implode('/', $pluginPart); - if ($plugin && Plugin::loaded($plugin)) { + if ($plugin && Plugin::isLoaded($plugin)) { $parts = array_slice($parts, $i + 1); $fileFragment = implode(DIRECTORY_SEPARATOR, $parts); $pluginWebroot = Plugin::path($plugin) . 'webroot' . DIRECTORY_SEPARATOR; diff --git a/app/vendor/cakephp/cakephp/src/Routing/Middleware/AssetMiddleware.php b/app/vendor/cakephp/cakephp/src/Routing/Middleware/AssetMiddleware.php index 50f83960a..de40cc267 100644 --- a/app/vendor/cakephp/cakephp/src/Routing/Middleware/AssetMiddleware.php +++ b/app/vendor/cakephp/cakephp/src/Routing/Middleware/AssetMiddleware.php @@ -130,7 +130,7 @@ protected function isNotModified($request, $file) * Builds asset file path based off url * * @param string $url Asset URL - * @return string Absolute path for asset file + * @return string|null Absolute path for asset file, null on failure */ protected function _getAssetFile($url) { @@ -142,7 +142,7 @@ protected function _getAssetFile($url) } $pluginPart[] = Inflector::camelize($parts[$i]); $plugin = implode('/', $pluginPart); - if ($plugin && Plugin::loaded($plugin)) { + if ($plugin && Plugin::isLoaded($plugin)) { $parts = array_slice($parts, $i + 1); $fileFragment = implode(DIRECTORY_SEPARATOR, $parts); $pluginWebroot = Plugin::path($plugin) . 'webroot' . DIRECTORY_SEPARATOR; @@ -151,7 +151,7 @@ protected function _getAssetFile($url) } } - return ''; + return null; } /** diff --git a/app/vendor/cakephp/cakephp/src/Routing/Middleware/RoutingMiddleware.php b/app/vendor/cakephp/cakephp/src/Routing/Middleware/RoutingMiddleware.php index 6d459595c..f9a71e40d 100644 --- a/app/vendor/cakephp/cakephp/src/Routing/Middleware/RoutingMiddleware.php +++ b/app/vendor/cakephp/cakephp/src/Routing/Middleware/RoutingMiddleware.php @@ -15,8 +15,8 @@ namespace Cake\Routing\Middleware; use Cake\Cache\Cache; +use Cake\Core\HttpApplicationInterface; use Cake\Core\PluginApplicationInterface; -use Cake\Http\BaseApplication; use Cake\Http\MiddlewareQueue; use Cake\Http\Runner; use Cake\Routing\Exception\RedirectException; @@ -39,7 +39,7 @@ class RoutingMiddleware /** * The application that will have its routing hook invoked. * - * @var \Cake\Http\BaseApplication + * @var \Cake\Core\HttpApplicationInterface|null */ protected $app; @@ -47,18 +47,24 @@ class RoutingMiddleware * The cache configuration name to use for route collection caching, * null to disable caching * - * @var string + * @var string|null */ protected $cacheConfig; /** * Constructor * - * @param \Cake\Http\BaseApplication $app The application instance that routes are defined on. + * @param \Cake\Core\HttpApplicationInterface|null $app The application instance that routes are defined on. * @param string|null $cacheConfig The cache config name to use or null to disable routes cache */ - public function __construct(BaseApplication $app = null, $cacheConfig = null) + public function __construct(HttpApplicationInterface $app = null, $cacheConfig = null) { + if ($app === null) { + deprecationWarning( + 'RoutingMiddleware should be passed an application instance. ' . + 'Failing to do so can cause plugin routes to not behave correctly.' + ); + } $this->app = $app; $this->cacheConfig = $cacheConfig; } diff --git a/app/vendor/cakephp/cakephp/src/Routing/RequestActionTrait.php b/app/vendor/cakephp/cakephp/src/Routing/RequestActionTrait.php index 1cc2c2f1a..27f535002 100644 --- a/app/vendor/cakephp/cakephp/src/Routing/RequestActionTrait.php +++ b/app/vendor/cakephp/cakephp/src/Routing/RequestActionTrait.php @@ -108,10 +108,11 @@ public function requestAction($url, array $extra = []) if (empty($url)) { return false; } - if (($index = array_search('return', $extra)) !== false) { + $isReturn = array_search('return', $extra, true); + if ($isReturn !== false) { $extra['return'] = 0; $extra['autoRender'] = 1; - unset($extra[$index]); + unset($extra[$isReturn]); } $extra += ['autoRender' => 0, 'return' => 1, 'bare' => 1, 'requested' => 1]; diff --git a/app/vendor/cakephp/cakephp/src/Routing/Route/EntityRoute.php b/app/vendor/cakephp/cakephp/src/Routing/Route/EntityRoute.php index 97ac74f63..522b7e4c0 100644 --- a/app/vendor/cakephp/cakephp/src/Routing/Route/EntityRoute.php +++ b/app/vendor/cakephp/cakephp/src/Routing/Route/EntityRoute.php @@ -41,13 +41,15 @@ class EntityRoute extends Route */ public function match(array $url, array $context = []) { + if (empty($this->_compiledRoute)) { + $this->compile(); + } + if (isset($url['_entity'])) { $entity = $url['_entity']; $this->_checkEntity($entity); - preg_match_all('@:(\w+)@', $this->template, $matches); - - foreach ($matches[1] as $field) { + foreach ($this->keys as $field) { if (!isset($url[$field]) && isset($entity[$field])) { $url[$field] = $entity[$field]; } diff --git a/app/vendor/cakephp/cakephp/src/Routing/Route/RedirectRoute.php b/app/vendor/cakephp/cakephp/src/Routing/Route/RedirectRoute.php index e4ccc3d16..7de437913 100644 --- a/app/vendor/cakephp/cakephp/src/Routing/Route/RedirectRoute.php +++ b/app/vendor/cakephp/cakephp/src/Routing/Route/RedirectRoute.php @@ -108,4 +108,17 @@ public function match(array $url, array $context = []) { return false; } + + /** + * Sets the HTTP status + * + * @param int $status The status code for this route + * @return $this + */ + public function setStatus($status) + { + $this->options['status'] = $status; + + return $this; + } } diff --git a/app/vendor/cakephp/cakephp/src/Routing/Route/Route.php b/app/vendor/cakephp/cakephp/src/Routing/Route/Route.php index 77a59d263..d175e395a 100644 --- a/app/vendor/cakephp/cakephp/src/Routing/Route/Route.php +++ b/app/vendor/cakephp/cakephp/src/Routing/Route/Route.php @@ -83,7 +83,7 @@ class Route /** * List of connected extensions for this route. * - * @var array + * @var string[] */ protected $_extensions = []; @@ -161,7 +161,7 @@ public function extensions($extensions = null) /** * Set the supported extensions for this route. * - * @param array $extensions The extensions to set. + * @param string[] $extensions The extensions to set. * @return $this */ public function setExtensions(array $extensions) @@ -177,7 +177,7 @@ public function setExtensions(array $extensions) /** * Get the supported extensions for this route. * - * @return array + * @return string[] */ public function getExtensions() { @@ -187,7 +187,7 @@ public function getExtensions() /** * Set the accepted HTTP methods for this route. * - * @param array $methods The HTTP methods to accept. + * @param string[] $methods The HTTP methods to accept. * @return $this * @throws \InvalidArgumentException */ @@ -211,12 +211,12 @@ public function setMethods(array $methods) * If any of your patterns contain multibyte values, the `multibytePattern` * mode will be enabled. * - * @param array $patterns The patterns to apply to routing elements + * @param string[] $patterns The patterns to apply to routing elements * @return $this */ public function setPatterns(array $patterns) { - $patternValues = implode("", $patterns); + $patternValues = implode('', $patterns); if (mb_strlen($patternValues) < strlen($patternValues)) { $this->options['multibytePattern'] = true; } @@ -241,7 +241,7 @@ public function setHost($host) /** * Set the names of parameters that will be converted into passed parameters * - * @param array $names The names of the parameters that should be passed. + * @param string[] $names The names of the parameters that should be passed. * @return $this */ public function setPass(array $names) @@ -254,7 +254,7 @@ public function setPass(array $names) /** * Set the names of parameters that will persisted automatically * - * Persistent parametesr allow you to define which route parameters should be automatically + * Persistent parameters allow you to define which route parameters should be automatically * included when generating new URLs. You can override persistent parameters * by redefining them in a URL or remove them by setting the persistent parameter to `false`. * @@ -769,7 +769,7 @@ protected function _matchMethod($url) $url['_method'] = $url['[method]']; } if (empty($url['_method'])) { - return false; + $url['_method'] = 'GET'; } $methods = array_map('strtoupper', (array)$url['_method']); foreach ($methods as $value) { diff --git a/app/vendor/cakephp/cakephp/src/Routing/RouteBuilder.php b/app/vendor/cakephp/cakephp/src/Routing/RouteBuilder.php index 9409f1bce..5074ded44 100644 --- a/app/vendor/cakephp/cakephp/src/Routing/RouteBuilder.php +++ b/app/vendor/cakephp/cakephp/src/Routing/RouteBuilder.php @@ -69,7 +69,7 @@ class RouteBuilder /** * The extensions that should be set into the routes connected. * - * @var array + * @var string[] */ protected $_extensions = []; @@ -213,7 +213,7 @@ public function extensions($extensions = null) * Future routes connected in through this builder will have the connected * extensions applied. However, setting extensions does not modify existing routes. * - * @param string|array $extensions The extensions to set. + * @param string|string[] $extensions The extensions to set. * @return $this */ public function setExtensions($extensions) @@ -226,7 +226,7 @@ public function setExtensions($extensions) /** * Get the extensions in this route builder's scope. * - * @return array + * @return string[] */ public function getExtensions() { @@ -724,14 +724,9 @@ public function loadPlugin($name, $file = 'routes.php') public function connect($route, $defaults = [], array $options = []) { $defaults = $this->parseDefaults($defaults); - if (!isset($options['action']) && !isset($defaults['action'])) { - $defaults['action'] = 'index'; - } - if (empty($options['_ext'])) { $options['_ext'] = $this->_extensions; } - if (empty($options['routeClass'])) { $options['routeClass'] = $this->_routeClass; } @@ -823,6 +818,9 @@ protected function _makeRoute($route, $defaults, $options) } } $defaults += $this->_params + ['plugin' => null]; + if (!isset($defaults['action']) && !isset($options['action'])) { + $defaults['action'] = 'index'; + } $route = new $routeClass($route, $defaults, $options); } @@ -868,7 +866,7 @@ protected function _makeRoute($route, $defaults, $options) * @param array $options An array matching the named elements in the route to regular expressions which that * element should match. Also contains additional parameters such as which routed parameters should be * shifted into the passed arguments. As well as supplying patterns for routing parameters. - * @return void + * @return \Cake\Routing\Route\Route|\Cake\Routing\Route\RedirectRoute */ public function redirect($route, $url, array $options = []) { @@ -878,7 +876,8 @@ public function redirect($route, $url, array $options = []) if (is_string($url)) { $url = ['redirect' => $url]; } - $this->connect($route, $url, $options); + + return $this->connect($route, $url, $options); } /** @@ -1071,7 +1070,7 @@ public function applyMiddleware(...$names) * Apply a set of middleware to a group * * @param string $name Name of the middleware group - * @param array $middlewareNames Names of the middleware + * @param string[] $middlewareNames Names of the middleware * @return $this */ public function middlewareGroup($name, array $middlewareNames) diff --git a/app/vendor/cakephp/cakephp/src/Routing/RouteCollection.php b/app/vendor/cakephp/cakephp/src/Routing/RouteCollection.php index 30a6f6705..faafc9f15 100644 --- a/app/vendor/cakephp/cakephp/src/Routing/RouteCollection.php +++ b/app/vendor/cakephp/cakephp/src/Routing/RouteCollection.php @@ -83,7 +83,7 @@ class RouteCollection /** * Route extensions * - * @var array + * @var string[] */ protected $_extensions = []; @@ -393,7 +393,7 @@ public function extensions($extensions = null, $merge = true) /** * Get the extensions that can be handled. * - * @return array The valid extensions. + * @return string[] The valid extensions. */ public function getExtensions() { @@ -403,7 +403,7 @@ public function getExtensions() /** * Set the extensions that the route collection can handle. * - * @param array $extensions The list of extensions to set. + * @param string[] $extensions The list of extensions to set. * @param bool $merge Whether to merge with or override existing extensions. * Defaults to `true`. * @return $this @@ -430,6 +430,7 @@ public function setExtensions(array $extensions, $merge = true) * @param string $name The name of the middleware. Used when applying middleware to a scope. * @param callable|string $middleware The middleware callable or class name to register. * @return $this + * @throws \RuntimeException */ public function registerMiddleware($name, $middleware) { @@ -442,7 +443,7 @@ public function registerMiddleware($name, $middleware) * Add middleware to a middleware group * * @param string $name Name of the middleware group - * @param array $middlewareNames Names of the middleware + * @param string[] $middlewareNames Names of the middleware * @return $this */ public function middlewareGroup($name, array $middlewareNames) @@ -527,7 +528,7 @@ public function applyMiddleware($path, array $middleware) /** * Get an array of middleware given a list of names * - * @param array $names The names of the middleware or groups to fetch + * @param string[] $names The names of the middleware or groups to fetch * @return array An array of middleware. If any of the passed names are groups, * the groups middleware will be flattened into the returned list. * @throws \RuntimeException when a requested middleware does not exist. diff --git a/app/vendor/cakephp/cakephp/src/Routing/Router.php b/app/vendor/cakephp/cakephp/src/Routing/Router.php index 5cdba9f34..d6953dcdf 100644 --- a/app/vendor/cakephp/cakephp/src/Routing/Router.php +++ b/app/vendor/cakephp/cakephp/src/Routing/Router.php @@ -18,7 +18,12 @@ use Cake\Http\ServerRequest; use Cake\Routing\Exception\MissingRouteException; use Cake\Utility\Inflector; +use Exception; use Psr\Http\Message\ServerRequestInterface; +use ReflectionFunction; +use ReflectionMethod; +use RuntimeException; +use Throwable; /** * Parses the request URL into controller, action, and parameters. Uses the connected routes @@ -206,6 +211,7 @@ public static function connect($route, $defaults = [], $options = []) { static::$initialized = true; static::scope('/', function ($routes) use ($route, $defaults, $options) { + /** @var \Cake\Routing\RouteBuilder $routes */ $routes->connect($route, $defaults, $options); }); } @@ -216,7 +222,7 @@ public static function connect($route, $defaults = [], $options = []) * Compatibility proxy to \Cake\Routing\RouteBuilder::redirect() in the `/` scope. * * @param string $route A string describing the template of the route - * @param array $url A URL to redirect to. Can be a string or a Cake array-based URL + * @param array|string $url A URL to redirect to. Can be a string or a Cake array-based URL * @param array $options An array matching the named elements in the route to regular expressions which that * element should match. Also contains additional parameters such as which routed parameters should be * shifted into the passed arguments. As well as supplying patterns for routing parameters. @@ -313,6 +319,7 @@ public static function mapResources($controller, $options = []) } $callback = function ($routes) use ($name, $options) { + /** @var \Cake\Routing\RouteBuilder $routes */ $routes->resources($name, $options); }; @@ -514,6 +521,30 @@ public static function reload() static::$_collection = new RouteCollection(); } + /** + * Reset routes and related state. + * + * Similar to reload() except that this doesn't reset all global state, + * as that leads to incorrect behavior in some plugin test case scenarios. + * + * This method will reset: + * + * - routes + * - URL Filters + * - the initialized property + * + * Extensions and default route classes will not be modified + * + * @internal + * @return void + */ + public static function resetRoutes() + { + static::$_collection = new RouteCollection(); + static::$_urlFilters = []; + static::$initialized = false; + } + /** * Add a URL filter to Router. * @@ -559,8 +590,29 @@ public static function addUrlFilter(callable $function) protected static function _applyUrlFilters($url) { $request = static::getRequest(true); + $e = null; foreach (static::$_urlFilters as $filter) { - $url = $filter($url, $request); + try { + $url = $filter($url, $request); + } catch (Exception $e) { + // fall through + } catch (Throwable $e) { + // fall through + } + if ($e !== null) { + if (is_array($filter)) { + $ref = new ReflectionMethod($filter[0], $filter[1]); + } else { + $ref = new ReflectionFunction($filter); + } + $message = sprintf( + 'URL filter defined in %s on line %s could not be applied. The filter failed with: %s', + $ref->getFileName(), + $ref->getStartLine(), + $e->getMessage() + ); + throw new RuntimeException($message, $e->getCode(), $e); + } } return $url; diff --git a/app/vendor/cakephp/cakephp/src/Shell/CommandListShell.php b/app/vendor/cakephp/cakephp/src/Shell/CommandListShell.php index 44baa9b61..09054ce34 100644 --- a/app/vendor/cakephp/cakephp/src/Shell/CommandListShell.php +++ b/app/vendor/cakephp/cakephp/src/Shell/CommandListShell.php @@ -74,9 +74,9 @@ public function main() { if (!$this->param('xml') && !$this->param('version')) { $this->out('Current Paths:', 2); - $this->out('* app: ' . APP_DIR); - $this->out('* root: ' . rtrim(ROOT, DIRECTORY_SEPARATOR)); - $this->out('* core: ' . rtrim(CORE_PATH, DIRECTORY_SEPARATOR)); + $this->out('* app: ' . APP_DIR . DIRECTORY_SEPARATOR); + $this->out('* root: ' . ROOT . DIRECTORY_SEPARATOR); + $this->out('* core: ' . CORE_PATH); $this->out(''); $this->out('Available Shells:', 2); diff --git a/app/vendor/cakephp/cakephp/src/Shell/CompletionShell.php b/app/vendor/cakephp/cakephp/src/Shell/CompletionShell.php index 860c70460..a2843322c 100644 --- a/app/vendor/cakephp/cakephp/src/Shell/CompletionShell.php +++ b/app/vendor/cakephp/cakephp/src/Shell/CompletionShell.php @@ -168,7 +168,7 @@ public function getOptionParser() protected function _output($options = []) { if ($options) { - return $this->out(implode($options, ' ')); + return $this->out(implode(' ', $options)); } } } diff --git a/app/vendor/cakephp/cakephp/src/Shell/Helper/TableHelper.php b/app/vendor/cakephp/cakephp/src/Shell/Helper/TableHelper.php index 60132ae08..b8116b634 100644 --- a/app/vendor/cakephp/cakephp/src/Shell/Helper/TableHelper.php +++ b/app/vendor/cakephp/cakephp/src/Shell/Helper/TableHelper.php @@ -44,7 +44,7 @@ protected function _calculateWidths($rows) $widths = []; foreach ($rows as $line) { foreach (array_values($line) as $k => $v) { - $columnLength = mb_strwidth($v); + $columnLength = $this->_cellWidth($v); if ($columnLength >= (isset($widths[$k]) ? $widths[$k] : 0)) { $widths[$k] = $columnLength; } @@ -54,6 +54,26 @@ protected function _calculateWidths($rows) return $widths; } + /** + * Get the width of a cell exclusive of style tags. + * + * @param string $text The text to calculate a width for. + * @return int The width of the textual content in visible characters. + */ + protected function _cellWidth($text) + { + if (strpos($text, '<') === false && strpos($text, '>') === false) { + return mb_strwidth($text); + } + + /** @var array $styles */ + $styles = $this->_io->styles(); + $tags = implode('|', array_keys($styles)); + $text = preg_replace('##', '', $text); + + return mb_strwidth($text); + } + /** * Output a row separator. * @@ -86,7 +106,7 @@ protected function _render(array $row, $widths, $options = []) $out = ''; foreach (array_values($row) as $i => $column) { - $pad = $widths[$i] - mb_strwidth($column); + $pad = $widths[$i] - $this->_cellWidth($column); if (!empty($options['style'])) { $column = $this->_addStyle($column, $options['style']); } diff --git a/app/vendor/cakephp/cakephp/src/Shell/I18nShell.php b/app/vendor/cakephp/cakephp/src/Shell/I18nShell.php index 40828f227..4dd6e748d 100644 --- a/app/vendor/cakephp/cakephp/src/Shell/I18nShell.php +++ b/app/vendor/cakephp/cakephp/src/Shell/I18nShell.php @@ -15,6 +15,7 @@ namespace Cake\Shell; use Cake\Console\Shell; +use Cake\Core\App; use Cake\Core\Plugin; use Cake\Utility\Inflector; use DirectoryIterator; @@ -94,13 +95,13 @@ public function init($language = null) $this->abort('Invalid language code. Valid is `en`, `eng`, `en_US` etc.'); } - $this->_paths = [APP]; + $this->_paths = App::path('Locale'); if ($this->param('plugin')) { $plugin = Inflector::camelize($this->param('plugin')); - $this->_paths = [Plugin::classPath($plugin)]; + $this->_paths = App::path('Locale', $plugin); } - $response = $this->in('What folder?', null, rtrim($this->_paths[0], DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'Locale'); + $response = $this->in('What folder?', null, rtrim($this->_paths[0], DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR); $sourceFolder = rtrim($response, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; $targetFolder = $sourceFolder . $language . DIRECTORY_SEPARATOR; if (!is_dir($targetFolder)) { diff --git a/app/vendor/cakephp/cakephp/src/Shell/ServerShell.php b/app/vendor/cakephp/cakephp/src/Shell/ServerShell.php index 6bcdea17e..67b7ac3f2 100644 --- a/app/vendor/cakephp/cakephp/src/Shell/ServerShell.php +++ b/app/vendor/cakephp/cakephp/src/Shell/ServerShell.php @@ -82,7 +82,7 @@ public function startup() $this->_host = $this->param('host'); } if ($this->param('port')) { - $this->_port = $this->param('port'); + $this->_port = (int)$this->param('port'); } if ($this->param('document_root')) { $this->_documentRoot = $this->param('document_root'); diff --git a/app/vendor/cakephp/cakephp/src/Shell/Task/AssetsTask.php b/app/vendor/cakephp/cakephp/src/Shell/Task/AssetsTask.php index de193e533..23fd28021 100644 --- a/app/vendor/cakephp/cakephp/src/Shell/Task/AssetsTask.php +++ b/app/vendor/cakephp/cakephp/src/Shell/Task/AssetsTask.php @@ -88,7 +88,7 @@ protected function _list($name = null) if ($name === null) { $pluginsList = Plugin::loaded(); } else { - if (!Plugin::loaded($name)) { + if (!Plugin::isLoaded($name)) { $this->err(sprintf('Plugin %s is not loaded.', $name)); return []; diff --git a/app/vendor/cakephp/cakephp/src/Shell/Task/CommandTask.php b/app/vendor/cakephp/cakephp/src/Shell/Task/CommandTask.php index 12874ac07..e45281b82 100644 --- a/app/vendor/cakephp/cakephp/src/Shell/Task/CommandTask.php +++ b/app/vendor/cakephp/cakephp/src/Shell/Task/CommandTask.php @@ -68,7 +68,7 @@ public function getShellList() * @param array $shellList The shell listing array. * @param string $path The path to look in. * @param string $key The key to add shells to - * @param array $skip A list of commands to exclude. + * @param string[] $skip A list of commands to exclude. * @return array The updated list of shells. */ protected function _findShells($shellList, $path, $key, $skip) @@ -163,7 +163,7 @@ public function commands() * Return a list of subcommands for a given command * * @param string $commandName The command you want subcommands from. - * @return array + * @return string[] * @throws \ReflectionException */ public function subCommands($commandName) diff --git a/app/vendor/cakephp/cakephp/src/Shell/Task/ExtractTask.php b/app/vendor/cakephp/cakephp/src/Shell/Task/ExtractTask.php index 19127158c..c182bab97 100644 --- a/app/vendor/cakephp/cakephp/src/Shell/Task/ExtractTask.php +++ b/app/vendor/cakephp/cakephp/src/Shell/Task/ExtractTask.php @@ -16,6 +16,7 @@ use Cake\Console\Shell; use Cake\Core\App; +use Cake\Core\Exception\MissingPluginException; use Cake\Core\Plugin; use Cake\Filesystem\File; use Cake\Filesystem\Folder; @@ -48,6 +49,13 @@ class ExtractTask extends Shell */ protected $_merge = false; + /** + * Use relative paths in the pot files rather than full path + * + * @var bool + */ + protected $_relativePaths = false; + /** * Current file being processed * @@ -104,6 +112,18 @@ class ExtractTask extends Shell */ protected $_extractCore = false; + /** + * Displays marker error(s) if true + * @var bool + */ + protected $_markerError; + + /** + * Count number of marker errors found + * @var bool + */ + protected $_countMarkerError = 0; + /** * No welcome message. * @@ -168,8 +188,8 @@ public function main() $this->_paths = explode(',', $this->params['paths']); } elseif (isset($this->params['plugin'])) { $plugin = Inflector::camelize($this->params['plugin']); - if (!Plugin::loaded($plugin)) { - Plugin::load($plugin); + if (!Plugin::isLoaded($plugin)) { + throw new MissingPluginException(['plugin' => $plugin]); } $this->_paths = [Plugin::classPath($plugin)]; $this->params['plugin'] = $plugin; @@ -232,6 +252,9 @@ public function main() $this->_merge = strtolower($response) === 'y'; } + $this->_markerError = $this->param('marker-error'); + $this->_relativePaths = $this->param('relative-paths'); + if (empty($this->_files)) { $this->_searchFiles(); } @@ -300,6 +323,11 @@ protected function _extract() $this->_paths = $this->_files = $this->_storage = []; $this->_translations = $this->_tokens = []; $this->out(); + if ($this->_countMarkerError) { + $this->err("{$this->_countMarkerError} marker error(s) detected."); + $this->err(" => Use the --marker-error option to display errors."); + } + $this->out('Done.'); } @@ -320,6 +348,10 @@ public function getOptionParser() ])->addOption('merge', [ 'help' => 'Merge all domain strings into the default.po file.', 'choices' => ['yes', 'no'] + ])->addOption('relative-paths', [ + 'help' => 'Use relative paths in the .pot file', + 'boolean' => true, + 'default' => false, ])->addOption('output', [ 'help' => 'Full path to output directory.' ])->addOption('files', [ @@ -329,7 +361,8 @@ public function getOptionParser() 'default' => true, 'help' => 'Ignores all files in plugins if this command is run inside from the same app directory.' ])->addOption('plugin', [ - 'help' => 'Extracts tokens only from the plugin specified and puts the result in the plugin\'s Locale directory.' + 'help' => 'Extracts tokens only from the plugin specified and puts the result in the plugin\'s Locale directory.', + 'short' => 'p', ])->addOption('ignore-model-validation', [ 'boolean' => true, 'default' => false, @@ -352,6 +385,10 @@ public function getOptionParser() 'boolean' => true, 'default' => false, 'help' => 'Do not write file locations for each extracted message.', + ])->addOption('marker-error', [ + 'boolean' => true, + 'default' => false, + 'help' => 'Do not display marker error.', ]); return $parser; @@ -369,6 +406,18 @@ protected function _extractTokens() $progress->init(['total' => count($this->_files)]); $isVerbose = $this->param('verbose'); + $functions = [ + '__' => ['singular'], + '__n' => ['singular', 'plural'], + '__d' => ['domain', 'singular'], + '__dn' => ['domain', 'singular', 'plural'], + '__x' => ['context', 'singular'], + '__xn' => ['context', 'singular', 'plural'], + '__dx' => ['domain', 'context', 'singular'], + '__dxn' => ['domain', 'context', 'singular', 'plural'], + ]; + $pattern = '/(' . implode('|', array_keys($functions)) . ')\s*\(/'; + foreach ($this->_files as $file) { $this->_file = $file; if ($isVerbose) { @@ -376,23 +425,22 @@ protected function _extractTokens() } $code = file_get_contents($file); - $allTokens = token_get_all($code); - $this->_tokens = []; - foreach ($allTokens as $token) { - if (!is_array($token) || ($token[0] !== T_WHITESPACE && $token[0] !== T_INLINE_HTML)) { - $this->_tokens[] = $token; + if (preg_match($pattern, $code) === 1) { + $allTokens = token_get_all($code); + + $this->_tokens = []; + foreach ($allTokens as $token) { + if (!is_array($token) || ($token[0] !== T_WHITESPACE && $token[0] !== T_INLINE_HTML)) { + $this->_tokens[] = $token; + } + } + unset($allTokens); + + foreach ($functions as $functionName => $map) { + $this->_parse($functionName, $map); } } - unset($allTokens); - $this->_parse('__', ['singular']); - $this->_parse('__n', ['singular', 'plural']); - $this->_parse('__d', ['domain', 'singular']); - $this->_parse('__dn', ['domain', 'singular', 'plural']); - $this->_parse('__x', ['context', 'singular']); - $this->_parse('__xn', ['context', 'singular', 'plural']); - $this->_parse('__dx', ['domain', 'context', 'singular']); - $this->_parse('__dxn', ['domain', 'context', 'singular', 'plural']); if (!$isVerbose) { $progress->increment(1); @@ -439,21 +487,29 @@ protected function _parse($functionName, $map) $strings = $this->_getStrings($position, $mapCount); if ($mapCount === count($strings)) { - $singular = null; + $singular = $plural = $context = null; + /** + * @var string $singular + * @var string|null $plural + * @var string|null $context + */ extract(array_combine($map, $strings)); $domain = isset($domain) ? $domain : 'default'; $details = [ 'file' => $this->_file, 'line' => $line, ]; - if (isset($plural)) { + if ($this->_relativePaths) { + $details['file'] = '.' . str_replace(ROOT, '', $details['file']); + } + if ($plural !== null) { $details['msgid_plural'] = $plural; } - if (isset($context)) { + if ($context !== null) { $details['msgctxt'] = $context; } $this->_addTranslation($domain, $singular, $details); - } elseif (strpos($this->_file, CAKE_CORE_INCLUDE_PATH) === false) { + } else { $this->_markerError($this->_file, $line, $functionName, $count); } } @@ -480,14 +536,18 @@ protected function _buildFiles() foreach ($contexts as $context => $details) { $plural = $details['msgid_plural']; $files = $details['references']; - $occurrences = []; - foreach ($files as $file => $lines) { - $lines = array_unique($lines); - $occurrences[] = $file . ':' . implode(';', $lines); - } - $occurrences = implode("\n#: ", $occurrences); $header = ''; + if (!$this->param('no-location')) { + $occurrences = []; + foreach ($files as $file => $lines) { + $lines = array_unique($lines); + foreach ($lines as $line) { + $occurrences[] = $file . ':' . $line; + } + } + $occurrences = implode("\n#: ", $occurrences); + $header = '#: ' . str_replace(DIRECTORY_SEPARATOR, '/', str_replace($paths, '', $occurrences)) . "\n"; } @@ -674,6 +734,14 @@ protected function _formatString($string) */ protected function _markerError($file, $line, $marker, $count) { + if (strpos($this->_file, CAKE_CORE_INCLUDE_PATH) === false) { + $this->_countMarkerError++; + } + + if (!$this->_markerError) { + return; + } + $this->err(sprintf("Invalid marker content in %s:%s\n* %s(", $file, $line, $marker)); $count += 2; $tokenCount = count($this->_tokens); diff --git a/app/vendor/cakephp/cakephp/src/Template/Element/plugin_class_error.ctp b/app/vendor/cakephp/cakephp/src/Template/Element/plugin_class_error.ctp index aebe9e831..fe52e5110 100644 --- a/app/vendor/cakephp/cakephp/src/Template/Element/plugin_class_error.ctp +++ b/app/vendor/cakephp/cakephp/src/Template/Element/plugin_class_error.ctp @@ -20,7 +20,7 @@ if (empty($plugin)) { echo '

'; -if (!Plugin::loaded($plugin)): +if (!Plugin::isLoaded($plugin)): echo sprintf('Make sure your plugin %s is in the %s directory and was loaded.', h($plugin), $pluginPath); else: echo sprintf('Make sure your plugin was loaded from %s and Composer is able to autoload its classes, see %s and %s', diff --git a/app/vendor/cakephp/cakephp/src/Template/Error/missing_behavior.ctp b/app/vendor/cakephp/cakephp/src/Template/Error/missing_behavior.ctp index 37c6189df..df86b5744 100644 --- a/app/vendor/cakephp/cakephp/src/Template/Error/missing_behavior.ctp +++ b/app/vendor/cakephp/cakephp/src/Template/Error/missing_behavior.ctp @@ -26,10 +26,10 @@ $pluginDot = empty($plugin) ? null : $plugin . '.'; if (empty($plugin)) { $filePath = APP_DIR . DIRECTORY_SEPARATOR; } -if (!empty($plugin) && Plugin::loaded($plugin)) { +if (!empty($plugin) && Plugin::isLoaded($plugin)) { $filePath = Plugin::classPath($plugin); } -if (!empty($plugin) && !Plugin::loaded($plugin)) { +if (!empty($plugin) && !Plugin::isLoaded($plugin)) { $filePath = $pluginPath . h($plugin) . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR; } diff --git a/app/vendor/cakephp/cakephp/src/Template/Error/missing_cell_view.ctp b/app/vendor/cakephp/cakephp/src/Template/Error/missing_cell_view.ctp index ad59435f7..6a721f223 100644 --- a/app/vendor/cakephp/cakephp/src/Template/Error/missing_cell_view.ctp +++ b/app/vendor/cakephp/cakephp/src/Template/Error/missing_cell_view.ctp @@ -20,13 +20,13 @@ $this->assign('templateName', 'missing_cell_view.ctp'); $this->assign('title', 'Missing Cell View'); $this->start('subheading'); -printf('The view for %sCell was not be found.', h(Inflector::camelize($name))); +printf('The view for %sCell was not be found.', h($name)); $this->end(); $this->start('file'); ?>

- Confirm you have created the file: "_ext) ?>" + Confirm you have created the file: "" in one of the following paths:

    @@ -36,7 +36,7 @@ $this->start('file'); if (strpos($path, CORE_PATH) !== false) { continue; } - echo sprintf('
  • %sCell/%s/%s
  • ', h($path), h($name), h($file . $this->_ext)); + echo sprintf('
  • %sCell/%s/%s
  • ', h($path), h($name), h($file)); endforeach; ?>
diff --git a/app/vendor/cakephp/cakephp/src/Template/Error/missing_component.ctp b/app/vendor/cakephp/cakephp/src/Template/Error/missing_component.ctp index d9ff8ef53..f00f6111d 100644 --- a/app/vendor/cakephp/cakephp/src/Template/Error/missing_component.ctp +++ b/app/vendor/cakephp/cakephp/src/Template/Error/missing_component.ctp @@ -25,10 +25,10 @@ $pluginDot = empty($plugin) ? null : $plugin . '.'; if (empty($plugin)) { $filePath = APP_DIR . DIRECTORY_SEPARATOR; } -if (!empty($plugin) && Plugin::loaded($plugin)) { +if (!empty($plugin) && Plugin::isLoaded($plugin)) { $filePath = Plugin::classPath($plugin); } -if (!empty($plugin) && !Plugin::loaded($plugin)) { +if (!empty($plugin) && !Plugin::isLoaded($plugin)) { $filePath = $pluginPath . h($plugin) . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR; } diff --git a/app/vendor/cakephp/cakephp/src/Template/Error/missing_helper.ctp b/app/vendor/cakephp/cakephp/src/Template/Error/missing_helper.ctp index 1bbecba16..a7a7850f2 100644 --- a/app/vendor/cakephp/cakephp/src/Template/Error/missing_helper.ctp +++ b/app/vendor/cakephp/cakephp/src/Template/Error/missing_helper.ctp @@ -25,10 +25,10 @@ $pluginDot = empty($plugin) ? null : $plugin . '.'; if (empty($plugin)) { $filePath = APP_DIR . DIRECTORY_SEPARATOR; } -if (!empty($plugin) && Plugin::loaded($plugin)) { +if (!empty($plugin) && Plugin::isLoaded($plugin)) { $filePath = Plugin::classPath($plugin); } -if (!empty($plugin) && !Plugin::loaded($plugin)) { +if (!empty($plugin) && !Plugin::isLoaded($plugin)) { $filePath = $pluginPath . h($plugin) . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR; } diff --git a/app/vendor/cakephp/cakephp/src/Template/Error/missing_view.ctp b/app/vendor/cakephp/cakephp/src/Template/Error/missing_view.ctp index 99d385fb9..f92d51736 100644 --- a/app/vendor/cakephp/cakephp/src/Template/Error/missing_view.ctp +++ b/app/vendor/cakephp/cakephp/src/Template/Error/missing_view.ctp @@ -23,10 +23,10 @@ if (empty($plugin)) { $filePath = APP_DIR . DIRECTORY_SEPARATOR; $namespace = str_replace('/', '\\', $plugin); } -if (!empty($plugin) && Plugin::loaded($plugin)) { +if (!empty($plugin) && Plugin::isLoaded($plugin)) { $filePath = Plugin::classPath($plugin); } -if (!empty($plugin) && !Plugin::loaded($plugin)) { +if (!empty($plugin) && !Plugin::isLoaded($plugin)) { $filePath = $pluginPath . h($plugin) . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR; } @@ -38,7 +38,7 @@ $this->start('subheading'); ?> Error: could not be found. - + Make sure your plugin is in the directory and was loaded. element('plugin_class_error', ['pluginPath' => $pluginPath]) ?> diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/ConsoleIntegrationTestCase.php b/app/vendor/cakephp/cakephp/src/TestSuite/ConsoleIntegrationTestCase.php index 34a2f07d1..25125c27b 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/ConsoleIntegrationTestCase.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/ConsoleIntegrationTestCase.php @@ -13,312 +13,13 @@ */ namespace Cake\TestSuite; -use Cake\Console\CommandRunner; -use Cake\Console\ConsoleInput; -use Cake\Console\ConsoleIo; -use Cake\Console\Exception\StopException; -use Cake\Core\Configure; -use Cake\TestSuite\Stub\ConsoleOutput; - /** * A test case class intended to make integration tests of cake console commands * easier. + * + * @deprecated 3.7.0 Use Cake\TestSuite\ConsoleIntegrationTestTrait instead */ abstract class ConsoleIntegrationTestCase extends TestCase { - /** - * Whether or not to use the CommandRunner - * - * @var bool - */ - protected $_useCommandRunner = false; - - /** - * Last exit code - * - * @var int|null - */ - protected $_exitCode; - - /** - * Console output stub - * - * @var \Cake\TestSuite\Stub\ConsoleOutput|\PHPUnit_Framework_MockObject_MockObject|null - */ - protected $_out; - - /** - * Console error output stub - * - * @var \Cake\TestSuite\Stub\ConsoleOutput|\PHPUnit_Framework_MockObject_MockObject|null - */ - protected $_err; - - /** - * Console input mock - * - * @var \Cake\Console\ConsoleInput|\PHPUnit_Framework_MockObject_MockObject|null - */ - protected $_in; - - /** - * Runs cli integration test - * - * @param string $command Command to run - * @param array $input Input values to pass to an interactive shell - * @return void - */ - public function exec($command, array $input = []) - { - $runner = $this->_makeRunner(); - - $this->_out = new ConsoleOutput(); - $this->_err = new ConsoleOutput(); - $this->_in = $this->getMockBuilder(ConsoleInput::class) - ->disableOriginalConstructor() - ->setMethods(['read']) - ->getMock(); - - $i = 0; - foreach ($input as $in) { - $this->_in - ->expects($this->at($i++)) - ->method('read') - ->will($this->returnValue($in)); - } - - $args = $this->_commandStringToArgs("cake $command"); - - $io = new ConsoleIo($this->_out, $this->_err, $this->_in); - - try { - $this->_exitCode = $runner->run($args, $io); - } catch (StopException $exception) { - $this->_exitCode = $exception->getCode(); - } - } - - /** - * tearDown - * - * @return void - */ - public function tearDown() - { - parent::tearDown(); - - $this->_exitCode = null; - $this->_out = null; - $this->_err = null; - $this->_in = null; - $this->_useCommandRunner = false; - } - - /** - * Set this test case to use the CommandRunner rather than the legacy - * ShellDispatcher - * - * @return void - */ - public function useCommandRunner() - { - $this->_useCommandRunner = true; - } - - /** - * Asserts shell exited with the expected code - * - * @param int $expected Expected exit code - * @param string $message Failure message to be appended to the generated message - * @return void - */ - public function assertExitCode($expected, $message = '') - { - $message = sprintf( - 'Shell exited with code %d instead of the expected code %d. %s', - $this->_exitCode, - $expected, - $message - ); - $this->assertSame($expected, $this->_exitCode, $message); - } - - /** - * Asserts that `stdout` is empty - * - * @param string $message The message to output when the assertion fails. - * @return void - */ - public function assertOutputEmpty($message = 'stdout was not empty') - { - $output = implode(PHP_EOL, $this->_out->messages()); - $this->assertSame('', $output, $message); - } - - /** - * Asserts `stdout` contains expected output - * - * @param string $expected Expected output - * @param string $message Failure message - * @return void - */ - public function assertOutputContains($expected, $message = '') - { - $output = implode(PHP_EOL, $this->_out->messages()); - $this->assertContains($expected, $output, $message); - } - /** - * Asserts `stdout` does not contain expected output - * - * @param string $expected Expected output - * @param string $message Failure message - * @return void - */ - public function assertOutputNotContains($expected, $message = '') - { - $output = implode(PHP_EOL, $this->_out->messages()); - $this->assertNotContains($expected, $output, $message); - } - - /** - * Asserts `stdout` contains expected regexp - * - * @param string $pattern Expected pattern - * @param string $message Failure message - * @return void - */ - public function assertOutputRegExp($pattern, $message = '') - { - $output = implode(PHP_EOL, $this->_out->messages()); - $this->assertRegExp($pattern, $output, $message); - } - - /** - * Check that a row of cells exists in the output. - * - * @param array $row Row of cells to ensure exist in the output. - * @param string $message Failure message. - * @return void - */ - protected function assertOutputContainsRow(array $row, $message = '') - { - $row = array_map(function ($cell) { - return preg_quote($cell, '/'); - }, $row); - $cells = implode('\s+\|\s+', $row); - $pattern = '/' . $cells . '/'; - $this->assertOutputRegExp($pattern); - } - - /** - * Asserts `stderr` contains expected output - * - * @param string $expected Expected output - * @param string $message Failure message - * @return void - */ - public function assertErrorContains($expected, $message = '') - { - $output = implode(PHP_EOL, $this->_err->messages()); - $this->assertContains($expected, $output, $message); - } - - /** - * Asserts `stderr` contains expected regexp - * - * @param string $pattern Expected pattern - * @param string $message Failure message - * @return void - */ - public function assertErrorRegExp($pattern, $message = '') - { - $output = implode(PHP_EOL, $this->_err->messages()); - $this->assertRegExp($pattern, $output, $message); - } - - /** - * Asserts that `stderr` is empty - * - * @param string $message The message to output when the assertion fails. - * @return void - */ - public function assertErrorEmpty($message = 'stderr was not empty') - { - $output = implode(PHP_EOL, $this->_err->messages()); - $this->assertSame('', $output, $message); - } - - /** - * Builds the appropriate command dispatcher - * - * @return CommandRunner|LegacyCommandRunner - */ - protected function _makeRunner() - { - if ($this->_useCommandRunner) { - $applicationClassName = Configure::read('App.namespace') . '\Application'; - /** @var \Cake\Http\BaseApplication $applicationClass */ - $applicationClass = new $applicationClassName(CONFIG); - - return new CommandRunner($applicationClass); - } - - return new LegacyCommandRunner(); - } - - /** - * Creates an $argv array from a command string - * - * @param string $command Command string - * @return array - */ - protected function _commandStringToArgs($command) - { - $charCount = strlen($command); - $argv = []; - $arg = ''; - $inDQuote = false; - $inSQuote = false; - for ($i = 0; $i < $charCount; $i++) { - $char = substr($command, $i, 1); - - // end of argument - if ($char === ' ' && !$inDQuote && !$inSQuote) { - if (strlen($arg)) { - $argv[] = $arg; - } - $arg = ''; - continue; - } - - // exiting single quote - if ($inSQuote && $char === "'") { - $inSQuote = false; - continue; - } - - // exiting double quote - if ($inDQuote && $char === '"') { - $inDQuote = false; - continue; - } - - // entering double quote - if ($char === '"' && !$inSQuote) { - $inDQuote = true; - continue; - } - - // entering single quote - if ($char === "'" && !$inDQuote) { - $inSQuote = true; - continue; - } - - $arg .= $char; - } - $argv[] = $arg; - - return $argv; - } + use ConsoleIntegrationTestTrait; } diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/ConsoleIntegrationTestTrait.php b/app/vendor/cakephp/cakephp/src/TestSuite/ConsoleIntegrationTestTrait.php new file mode 100644 index 000000000..9dd15475b --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/ConsoleIntegrationTestTrait.php @@ -0,0 +1,321 @@ +makeRunner(); + + $this->_out = new ConsoleOutput(); + $this->_err = new ConsoleOutput(); + $this->_in = new ConsoleInput($input); + + $args = $this->commandStringToArgs("cake $command"); + $io = new ConsoleIo($this->_out, $this->_err, $this->_in); + + try { + $this->_exitCode = $runner->run($args, $io); + } catch (StopException $exception) { + $this->_exitCode = $exception->getCode(); + } + } + + /** + * Cleans state to get ready for the next test + * + * @after + * @return void + */ + public function cleanupConsoleTrait() + { + $this->_exitCode = null; + $this->_out = null; + $this->_err = null; + $this->_in = null; + $this->_useCommandRunner = false; + } + + /** + * Set this test case to use the CommandRunner rather than the legacy + * ShellDispatcher + * + * @return void + */ + public function useCommandRunner() + { + $this->_useCommandRunner = true; + } + + /** + * Asserts shell exited with the expected code + * + * @param int $expected Expected exit code + * @param string $message Failure message + * @return void + */ + public function assertExitCode($expected, $message = '') + { + $this->assertThat($expected, new ExitCode($this->_exitCode), $message); + } + + /** + * Asserts shell exited with the Command::CODE_SUCCESS + * + * @param string $message Failure message + * @return void + */ + public function assertExitSuccess($message = '') + { + $this->assertThat(Command::CODE_SUCCESS, new ExitCode($this->_exitCode), $message); + } + + /** + * Asserts shell exited with Command::CODE_ERROR + * + * @param string $message Failure message + * @return void + */ + public function assertExitError($message = '') + { + $this->assertThat(Command::CODE_ERROR, new ExitCode($this->_exitCode), $message); + } + + /** + * Asserts that `stdout` is empty + * + * @param string $message The message to output when the assertion fails. + * @return void + */ + public function assertOutputEmpty($message = '') + { + $this->assertThat(null, new ContentsEmpty($this->_out->messages(), 'output'), $message); + } + + /** + * Asserts `stdout` contains expected output + * + * @param string $expected Expected output + * @param string $message Failure message + * @return void + */ + public function assertOutputContains($expected, $message = '') + { + $this->assertThat($expected, new ContentsContain($this->_out->messages(), 'output'), $message); + } + + /** + * Asserts `stdout` does not contain expected output + * + * @param string $expected Expected output + * @param string $message Failure message + * @return void + */ + public function assertOutputNotContains($expected, $message = '') + { + $this->assertThat($expected, new ContentsNotContain($this->_out->messages(), 'output'), $message); + } + + /** + * Asserts `stdout` contains expected regexp + * + * @param string $pattern Expected pattern + * @param string $message Failure message + * @return void + */ + public function assertOutputRegExp($pattern, $message = '') + { + $this->assertThat($pattern, new ContentsRegExp($this->_out->messages(), 'output'), $message); + } + + /** + * Check that a row of cells exists in the output. + * + * @param array $row Row of cells to ensure exist in the output. + * @param string $message Failure message. + * @return void + */ + protected function assertOutputContainsRow(array $row, $message = '') + { + $this->assertThat($row, new ContentsContainRow($this->_out->messages(), 'output'), $message); + } + + /** + * Asserts `stderr` contains expected output + * + * @param string $expected Expected output + * @param string $message Failure message + * @return void + */ + public function assertErrorContains($expected, $message = '') + { + $this->assertThat($expected, new ContentsContain($this->_err->messages(), 'error output'), $message); + } + + /** + * Asserts `stderr` contains expected regexp + * + * @param string $pattern Expected pattern + * @param string $message Failure message + * @return void + */ + public function assertErrorRegExp($pattern, $message = '') + { + $this->assertThat($pattern, new ContentsRegExp($this->_err->messages(), 'error output'), $message); + } + + /** + * Asserts that `stderr` is empty + * + * @param string $message The message to output when the assertion fails. + * @return void + */ + public function assertErrorEmpty($message = '') + { + $this->assertThat(null, new ContentsEmpty($this->_err->messages(), 'error output'), $message); + } + + /** + * Builds the appropriate command dispatcher + * + * @return CommandRunner|LegacyCommandRunner + */ + protected function makeRunner() + { + if ($this->_useCommandRunner) { + $applicationClassName = Configure::read('App.namespace') . '\Application'; + + return new CommandRunner(new $applicationClassName(CONFIG)); + } + + return new LegacyCommandRunner(); + } + + /** + * Creates an $argv array from a command string + * + * @param string $command Command string + * @return array + */ + protected function commandStringToArgs($command) + { + $charCount = strlen($command); + $argv = []; + $arg = ''; + $inDQuote = false; + $inSQuote = false; + for ($i = 0; $i < $charCount; $i++) { + $char = substr($command, $i, 1); + + // end of argument + if ($char === ' ' && !$inDQuote && !$inSQuote) { + if (strlen($arg)) { + $argv[] = $arg; + } + $arg = ''; + continue; + } + + // exiting single quote + if ($inSQuote && $char === "'") { + $inSQuote = false; + continue; + } + + // exiting double quote + if ($inDQuote && $char === '"') { + $inDQuote = false; + continue; + } + + // entering double quote + if ($char === '"' && !$inSQuote) { + $inDQuote = true; + continue; + } + + // entering single quote + if ($char === "'" && !$inDQuote) { + $inSQuote = true; + continue; + } + + $arg .= $char; + } + $argv[] = $arg; + + return $argv; + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ContentsBase.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ContentsBase.php new file mode 100644 index 000000000..4b4818604 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ContentsBase.php @@ -0,0 +1,49 @@ +contents = implode(PHP_EOL, $contents); + $this->output = $output; + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ContentsContain.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ContentsContain.php new file mode 100644 index 000000000..c51873852 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ContentsContain.php @@ -0,0 +1,44 @@ +contents, $other) !== false; + } + + /** + * Assertion message + * + * @return string + */ + public function toString() + { + return sprintf('is in %s', $this->output); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ContentsContainRow.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ContentsContainRow.php new file mode 100644 index 000000000..e73bb2019 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ContentsContainRow.php @@ -0,0 +1,59 @@ +contents) > 0; + } + + /** + * Assertion message + * + * @return string + */ + public function toString() + { + return sprintf('row was in %s', $this->output); + } + + /** + * @param mixed $other Expected content + * @return string + */ + public function failureDescription($other) + { + return $this->exporter->shortenedExport($other) . ' ' . $this->toString(); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ContentsEmpty.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ContentsEmpty.php new file mode 100644 index 000000000..e9bccc440 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ContentsEmpty.php @@ -0,0 +1,55 @@ +contents === ''; + } + + /** + * Assertion message + * + * @return string + */ + public function toString() + { + return sprintf('%s is empty', $this->output); + } + + /** + * Overwrites the descriptions so we can remove the automatic "expected" message + * + * @param mixed $other Value + * @return string + */ + protected function failureDescription($other) + { + return $this->toString(); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ContentsNotContain.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ContentsNotContain.php new file mode 100644 index 000000000..22e066b29 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ContentsNotContain.php @@ -0,0 +1,44 @@ +contents, $other) === false; + } + + /** + * Assertion message + * + * @return string + */ + public function toString() + { + return sprintf('is not in %s', $this->output); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ContentsRegExp.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ContentsRegExp.php new file mode 100644 index 000000000..44b5690ce --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ContentsRegExp.php @@ -0,0 +1,53 @@ +contents) > 0; + } + + /** + * Assertion message + * + * @return string + */ + public function toString() + { + return sprintf('PCRE pattern found in %s', $this->output); + } + + /** + * @param mixed $other Expected + * @return string + */ + public function failureDescription($other) + { + return $other . ' ' . $this->toString(); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ExitCode.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ExitCode.php new file mode 100644 index 000000000..c7bcd927f --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ExitCode.php @@ -0,0 +1,63 @@ +exitCode = $exitCode; + } + + /** + * Checks if event is in fired array + * + * @param mixed $other Constraint check + * @return bool + */ + public function matches($other) + { + return $other === $this->exitCode; + } + + /** + * Assertion message string + * + * @return string + */ + public function toString() + { + return sprintf('matches exit code %d', $this->exitCode); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailConstraintBase.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailConstraintBase.php new file mode 100644 index 000000000..356f2e320 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailConstraintBase.php @@ -0,0 +1,61 @@ +at = $at; + parent::__construct(); + } + + /** + * Gets the email or emails to check + * + * @return \Cake\Mailer\Email[] + */ + public function getEmails() + { + $emails = TestEmailTransport::getEmails(); + + if ($this->at) { + if (!isset($emails[$this->at])) { + return []; + } + + return [$emails[$this->at]]; + } + + return $emails; + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailContains.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailContains.php new file mode 100644 index 000000000..a4a4e55ae --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailContains.php @@ -0,0 +1,66 @@ +getEmails(); + foreach ($emails as $email) { + $message = implode("\r\n", (array)$email->message($this->type)); + + $other = preg_quote($other, '/'); + if (preg_match("/$other/", $message) > 0) { + return true; + } + } + + return false; + } + + /** + * Assertion message string + * + * @return string + */ + public function toString() + { + if ($this->at) { + return sprintf('is in email #%d', $this->at); + } + + return 'is in an email'; + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailContainsHtml.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailContainsHtml.php new file mode 100644 index 000000000..ff87bfb2d --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailContainsHtml.php @@ -0,0 +1,41 @@ +at) { + return sprintf('is in the html message of email #%d', $this->at); + } + + return 'is in the html message of an email'; + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailContainsText.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailContainsText.php new file mode 100644 index 000000000..1e1d773ec --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailContainsText.php @@ -0,0 +1,41 @@ +at) { + return sprintf('is in the text message of email #%d', $this->at); + } + + return 'is in the text message of an email'; + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailCount.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailCount.php new file mode 100644 index 000000000..34f02e5c4 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailCount.php @@ -0,0 +1,45 @@ +getEmails()) === $other; + } + + /** + * Assertion message string + * + * @return string + */ + public function toString() + { + return 'emails were sent'; + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailSentFrom.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailSentFrom.php new file mode 100644 index 000000000..7ddb6e0a7 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailSentFrom.php @@ -0,0 +1,39 @@ +at) { + return sprintf('sent email #%d', $this->at); + } + + return 'sent an email'; + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailSentTo.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailSentTo.php new file mode 100644 index 000000000..910903fee --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailSentTo.php @@ -0,0 +1,39 @@ +at) { + return sprintf('was sent email #%d', $this->at); + } + + return 'was sent an email'; + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailSentWith.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailSentWith.php new file mode 100644 index 000000000..dc785026b --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailSentWith.php @@ -0,0 +1,76 @@ +method = $method; + } + parent::__construct($at); + } + + /** + * Checks constraint + * + * @param mixed $other Constraint check + * @return bool + */ + public function matches($other) + { + $emails = $this->getEmails(); + foreach ($emails as $email) { + $value = $email->{'get' . ucfirst($this->method)}(); + if (in_array($this->method, ['to', 'cc', 'bcc', 'from']) && isset($value[$other])) { + return true; + } + if ($value === $other) { + return true; + } + } + + return false; + } + + /** + * Assertion message string + * + * @return string + */ + public function toString() + { + if ($this->at) { + return sprintf('is in email #%d `%s`', $this->at, $this->method); + } + + return sprintf('is in an email `%s`', $this->method); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/NoMailSent.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/NoMailSent.php new file mode 100644 index 000000000..29aa08a49 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/NoMailSent.php @@ -0,0 +1,56 @@ +getEmails()) === 0; + } + + /** + * Assertion message string + * + * @return string + */ + public function toString() + { + return 'no emails were sent'; + } + + /** + * Overwrites the descriptions so we can remove the automatic "expected" message + * + * @param mixed $other Value + * @return string + */ + protected function failureDescription($other) + { + return $this->toString(); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/EventFired.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/EventFired.php index c3fe9e177..68658863d 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/EventFired.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/EventFired.php @@ -1,22 +1,13 @@ ignoreCase = $ignoreCase; + } + + /** + * Checks assertion + * + * @param mixed $other Expected type + * @return bool + */ + public function matches($other) + { + $method = 'mb_strpos'; + if ($this->ignoreCase) { + $method = 'mb_stripos'; + } + + return $method($this->_getBodyAsString(), $other) !== false; + } + + /** + * Assertion message + * + * @return string + */ + public function toString() + { + return 'is in response body'; + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/BodyEmpty.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/BodyEmpty.php new file mode 100644 index 000000000..a0c0c4140 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/BodyEmpty.php @@ -0,0 +1,55 @@ +_getBodyAsString()); + } + + /** + * Assertion message + * + * @return string + */ + public function toString() + { + return 'response body is empty'; + } + + /** + * Overwrites the descriptions so we can remove the automatic "expected" message + * + * @param mixed $other Value + * @return string + */ + protected function failureDescription($other) + { + return $this->toString(); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/BodyEquals.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/BodyEquals.php new file mode 100644 index 000000000..df817b6cb --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/BodyEquals.php @@ -0,0 +1,44 @@ +_getBodyAsString() === $other; + } + + /** + * Assertion message + * + * @return string + */ + public function toString() + { + return 'matches response body'; + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/BodyNotContains.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/BodyNotContains.php new file mode 100644 index 000000000..281f963ad --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/BodyNotContains.php @@ -0,0 +1,44 @@ +_getBodyAsString()) > 0; + } + + /** + * Assertion message + * + * @return string + */ + public function toString() + { + return 'PCRE pattern found in response body'; + } + + /** + * @param mixed $other Expected + * @return string + */ + public function failureDescription($other) + { + return $other . ' ' . $this->toString(); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/ContentType.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/ContentType.php new file mode 100644 index 000000000..29d5f39f2 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/ContentType.php @@ -0,0 +1,49 @@ +response->getMimeType($other); + if ($alias !== false) { + $other = $alias; + } + + return $other === $this->response->getType(); + } + + /** + * Assertion message + * + * @return string + */ + public function toString() + { + return 'was set as the Content-Type'; + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/CookieEncryptedEquals.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/CookieEncryptedEquals.php new file mode 100644 index 000000000..660f42b27 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/CookieEncryptedEquals.php @@ -0,0 +1,87 @@ +key = $key; + $this->mode = $mode; + } + + /** + * Checks assertion + * + * @param mixed $other Expected content + * @return bool + */ + public function matches($other) + { + $cookie = $this->response->getCookie($this->cookieName); + + return $this->_decrypt($cookie['value'], $this->mode) === $other; + } + + /** + * Assertion message + * + * @return string + */ + public function toString() + { + return sprintf('was encrypted in cookie \'%s\'', $this->cookieName); + } + + /** + * Returns the encryption key + * + * @return string + */ + protected function _getCookieEncryptionKey() + { + return $this->key; + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/CookieEquals.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/CookieEquals.php new file mode 100644 index 000000000..5a69a9911 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/CookieEquals.php @@ -0,0 +1,65 @@ +cookieName = $cookieName; + } + + /** + * Checks assertion + * + * @param mixed $other Expected content + * @return bool + */ + public function matches($other) + { + $cookie = $this->response->getCookie($this->cookieName); + + return $cookie['value'] === $other; + } + + /** + * Assertion message + * + * @return string + */ + public function toString() + { + return sprintf('was in cookie \'%s\'', $this->cookieName); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/CookieNotSet.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/CookieNotSet.php new file mode 100644 index 000000000..6b041ad54 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/CookieNotSet.php @@ -0,0 +1,44 @@ +response->getCookie($other); + + return $cookie !== null && $cookie['value'] !== ''; + } + + /** + * Assertion message + * + * @return string + */ + public function toString() + { + return 'cookie was set'; + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/FileSent.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/FileSent.php new file mode 100644 index 000000000..d0e1ddc3d --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/FileSent.php @@ -0,0 +1,55 @@ +response->getFile() !== null; + } + + /** + * Assertion message + * + * @return string + */ + public function toString() + { + return 'file was sent'; + } + + /** + * Overwrites the descriptions so we can remove the automatic "expected" message + * + * @param mixed $other Value + * @return string + */ + protected function failureDescription($other) + { + return $this->toString(); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/FileSentAs.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/FileSentAs.php new file mode 100644 index 000000000..f1bd05234 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/FileSentAs.php @@ -0,0 +1,44 @@ +response->getFile()->path === $other; + } + + /** + * Assertion message + * + * @return string + */ + public function toString() + { + return 'file was sent'; + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/HeaderContains.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/HeaderContains.php new file mode 100644 index 000000000..ddde398e9 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/HeaderContains.php @@ -0,0 +1,44 @@ +response->getHeaderLine($this->headerName), $other) !== false; + } + + /** + * Assertion message + * + * @return string + */ + public function toString() + { + return sprintf('is in header \'%s\'', $this->headerName); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/HeaderEquals.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/HeaderEquals.php new file mode 100644 index 000000000..387e975ea --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/HeaderEquals.php @@ -0,0 +1,63 @@ +headerName = $headerName; + } + + /** + * Checks assertion + * + * @param mixed $other Expected content + * @return bool + */ + public function matches($other) + { + return $this->response->getHeaderLine($this->headerName) === $other; + } + + /** + * Assertion message + * + * @return string + */ + public function toString() + { + return sprintf('equals content in header \'%s\'', $this->headerName); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/HeaderNotContains.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/HeaderNotContains.php new file mode 100644 index 000000000..1851cff49 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/HeaderNotContains.php @@ -0,0 +1,44 @@ +headerName); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/HeaderNotSet.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/HeaderNotSet.php new file mode 100644 index 000000000..2f61e3805 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/HeaderNotSet.php @@ -0,0 +1,43 @@ +headerName); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/HeaderSet.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/HeaderSet.php new file mode 100644 index 000000000..4e3910627 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/HeaderSet.php @@ -0,0 +1,74 @@ +headerName = $headerName; + } + + /** + * Checks assertion + * + * @param mixed $other Expected content + * @return bool + */ + public function matches($other) + { + return $this->response->hasHeader($this->headerName); + } + + /** + * Assertion message + * + * @return string + */ + public function toString() + { + return sprintf('response has header \'%s\'', $this->headerName); + } + + /** + * Overwrites the descriptions so we can remove the automatic "expected" message + * + * @param mixed $other Value + * @return string + */ + protected function failureDescription($other) + { + return $this->toString(); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/ResponseBase.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/ResponseBase.php new file mode 100644 index 000000000..e05bb3880 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/ResponseBase.php @@ -0,0 +1,57 @@ +response = $response; + } + + /** + * Get the response body as string + * + * @return string The response body. + */ + protected function _getBodyAsString() + { + return (string)$this->response->getBody(); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusCode.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusCode.php new file mode 100644 index 000000000..624c4c385 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusCode.php @@ -0,0 +1,44 @@ +response->getStatusCode()); + } + + /** + * Failure description + * + * @param mixed $other Expected code + * @return string + */ + public function failureDescription($other) + { + return $other . ' ' . $this->toString(); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusCodeBase.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusCodeBase.php new file mode 100644 index 000000000..4542867f9 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusCodeBase.php @@ -0,0 +1,70 @@ +code; + } + + if (is_array($other)) { + return $this->statusCodeBetween($other[0], $other[1]); + } + + return $this->response->getStatusCode() === $other; + } + + /** + * Helper for checking status codes + * + * @param int $min Min status code (inclusive) + * @param int $max Max status code (inclusive) + * @return bool + */ + protected function statusCodeBetween($min, $max) + { + return $this->response->getStatusCode() >= $min && $this->response->getStatusCode() <= $max; + } + + /** + * Overwrites the descriptions so we can remove the automatic "expected" message + * + * @param mixed $other Value + * @return string + */ + protected function failureDescription($other) + { + return $this->toString(); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusError.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusError.php new file mode 100644 index 000000000..b1ff484b7 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusError.php @@ -0,0 +1,35 @@ +response->getStatusCode()); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusFailure.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusFailure.php new file mode 100644 index 000000000..588ba1251 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusFailure.php @@ -0,0 +1,35 @@ +response->getStatusCode()); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusOk.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusOk.php new file mode 100644 index 000000000..d8a60f6fa --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusOk.php @@ -0,0 +1,35 @@ +response->getStatusCode()); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusSuccess.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusSuccess.php new file mode 100644 index 000000000..4ffcc07a5 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusSuccess.php @@ -0,0 +1,35 @@ +response->getStatusCode()); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Session/FlashParamEquals.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Session/FlashParamEquals.php new file mode 100644 index 000000000..e2a5e2200 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Session/FlashParamEquals.php @@ -0,0 +1,109 @@ +enableRetainFlashMessages()` has been enabled for the test.'; + throw new AssertionFailedError($message); + } + + $this->session = $session; + $this->key = $key; + $this->param = $param; + $this->at = $at; + } + + /** + * Compare to flash message(s) + * + * @param mixed $other Value to compare with + * @return bool + */ + public function matches($other) + { + $messages = (array)$this->session->read('Flash.' . $this->key); + if ($this->at) { + $messages = [$this->session->read('Flash.' . $this->key . '.' . $this->at)]; + } + + foreach ($messages as $message) { + if (!isset($message[$this->param])) { + continue; + } + if ($message[$this->param] === $other) { + return true; + } + } + + return false; + } + + /** + * Assertion message string + * + * @return string + */ + public function toString() + { + if ($this->at !== null) { + return sprintf('was in \'%s\' %s #%d', $this->key, $this->param, $this->at); + } + + return sprintf('was in \'%s\' %s', $this->key, $this->param); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Session/SessionEquals.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Session/SessionEquals.php new file mode 100644 index 000000000..4ed4840eb --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Session/SessionEquals.php @@ -0,0 +1,80 @@ +session = $session; + $this->path = $path; + } + + /** + * Compare session value + * + * @param mixed $other Value to compare with + * @return bool + */ + public function matches($other) + { + return $this->session->read($this->path) === $other; + } + + /** + * Assertion message + * + * @return string + */ + public function toString() + { + return sprintf('is in session path \'%s\'', $this->path); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/View/LayoutFileEquals.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/View/LayoutFileEquals.php new file mode 100644 index 000000000..3428b8789 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/View/LayoutFileEquals.php @@ -0,0 +1,33 @@ +filename); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/View/TemplateFileEquals.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/View/TemplateFileEquals.php new file mode 100644 index 000000000..6cc48397b --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/View/TemplateFileEquals.php @@ -0,0 +1,63 @@ +filename = $filename; + } + + /** + * Checks assertion + * + * @param mixed $other Expected filename + * @return bool + */ + public function matches($other) + { + return strpos($this->filename, $other) !== false; + } + + /** + * Assertion message + * + * @return string + */ + public function toString() + { + return sprintf('equals template file %s', $this->filename); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/EmailAssertTrait.php b/app/vendor/cakephp/cakephp/src/TestSuite/EmailAssertTrait.php index d0353a788..d272e4a2a 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/EmailAssertTrait.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/EmailAssertTrait.php @@ -23,6 +23,8 @@ * @method void assertSame($expected, $result, $message) * @method void assertTextContains($needle, $haystack, $message) * @method \PHPUnit_Framework_MockObject_MockBuilder getMockBuilder($className) + * + * @deprecated 3.7.0 Use Cake\TestSuite\EmailTrait instead */ trait EmailAssertTrait { @@ -70,7 +72,7 @@ public function getMockForMailer($className, array $methods = []) { $name = current(array_slice(explode('\\', $className), -1)); - if (!in_array('profile', $methods)) { + if (!in_array('profile', $methods, true)) { $methods[] = 'profile'; } diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/EmailTrait.php b/app/vendor/cakephp/cakephp/src/TestSuite/EmailTrait.php new file mode 100644 index 000000000..0a264c731 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/EmailTrait.php @@ -0,0 +1,232 @@ +assertThat($count, new MailCount(), $message); + } + + /** + * + * Asserts that no emails were sent + * + * @param string $message Message + * @return void + */ + public function assertNoMailSent($message = null) + { + $this->assertThat(null, new NoMailSent(), $message); + } + + /** + * Asserts an email at a specific index was sent to an address + * + * @param int $at Email index + * @param string $address Email address + * @param string $message Message + * @return void + */ + public function assertMailSentToAt($at, $address, $message = null) + { + $this->assertThat($address, new MailSentTo($at), $message); + } + + /** + * Asserts an email at a specific index was sent from an address + * + * @param int $at Email index + * @param string $address Email address + * @param string $message Message + * @return void + */ + public function assertMailSentFromAt($at, $address, $message = null) + { + $this->assertThat($address, new MailSentFrom($at), $message); + } + + /** + * Asserts an email at a specific index contains expected contents + * + * @param int $at Email index + * @param string $contents Contents + * @param string $message Message + * @return void + */ + public function assertMailContainsAt($at, $contents, $message = null) + { + $this->assertThat($contents, new MailContains($at), $message); + } + + /** + * Asserts an email at a specific index contains expected html contents + * + * @param int $at Email index + * @param string $contents Contents + * @param string $message Message + * @return void + */ + public function assertMailContainsHtmlAt($at, $contents, $message = null) + { + $this->assertThat($contents, new MailContainsHtml($at), $message); + } + + /** + * Asserts an email at a specific index contains expected text contents + * + * @param int $at Email index + * @param string $contents Contents + * @param string $message Message + * @return void + */ + public function assertMailContainsTextAt($at, $contents, $message = null) + { + $this->assertThat($contents, new MailContainsText($at), $message); + } + + /** + * Asserts an email at a specific index contains the expected value within an Email getter + * + * @param int $at Email index + * @param string $expected Contents + * @param string $parameter Email getter parameter (e.g. "cc", "subject") + * @param string $message Message + * @return void + */ + public function assertMailSentWithAt($at, $expected, $parameter, $message = null) + { + $this->assertThat($expected, new MailSentWith($at, $parameter), $message); + } + + /** + * Asserts an email was sent to an address + * + * @param string $address Email address + * @param string $message Message + * @return void + */ + public function assertMailSentTo($address, $message = null) + { + $this->assertThat($address, new MailSentTo(), $message); + } + + /** + * Asserts an email was sent from an address + * + * @param string $address Email address + * @param string $message Message + * @return void + */ + public function assertMailSentFrom($address, $message = null) + { + $this->assertThat($address, new MailSentFrom(), $message); + } + + /** + * Asserts an email contains expected contents + * + * @param string $contents Contents + * @param string $message Message + * @return void + */ + public function assertMailContains($contents, $message = null) + { + $this->assertThat($contents, new MailContains(), $message); + } + + /** + * Asserts an email contains expected html contents + * + * @param string $contents Contents + * @param string $message Message + * @return void + */ + public function assertMailContainsHtml($contents, $message = null) + { + $this->assertThat($contents, new MailContainsHtml(), $message); + } + + /** + * Asserts an email contains an expected text content + * + * @param string $expectedText Expected text. + * @param string $message Message to display if assertion fails. + * @return void + */ + public function assertMailContainsText($expectedText, $message = null) + { + $this->assertThat($expectedText, new MailContainsText(), $message); + } + + /** + * Asserts an email contains the expected value within an Email getter + * + * @param string $expected Contents + * @param string $parameter Email getter parameter (e.g. "cc", "subject") + * @param string $message Message + * @return void + */ + public function assertMailSentWith($expected, $parameter, $message = null) + { + $this->assertThat($expected, new MailSentWith(null, $parameter), $message); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureInjector.php b/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureInjector.php index 74edb48a3..3f5044fda 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureInjector.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureInjector.php @@ -50,7 +50,7 @@ class FixtureInjector extends BaseTestListener public function __construct(FixtureManager $manager) { if (isset($_SERVER['argv'])) { - $manager->setDebug(in_array('--debug', $_SERVER['argv'])); + $manager->setDebug(in_array('--debug', $_SERVER['argv'], true)); } $this->_fixtureManager = $manager; $this->_fixtureManager->shutDown(); diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureManager.php b/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureManager.php index 7f2d7e6fa..6817fcf7e 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureManager.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureManager.php @@ -190,19 +190,43 @@ protected function _loadFixtures($test) list($plugin, $name) = explode('.', $pathName); // Flip vendored plugin separators $path = str_replace('/', '\\', $plugin); + $uninflected = $path; $baseNamespace = Inflector::camelize(str_replace('\\', '\ ', $path)); + if ($baseNamespace !== $uninflected) { + deprecationWarning(sprintf( + 'Declaring fixtures in underscored format in TestCase::$fixtures is deprecated.' . "\n" . + 'Expected "%s" instead in "%s".', + str_replace('\\', '/', $baseNamespace), + get_class($test) + )); + } $additionalPath = null; } else { $baseNamespace = ''; $name = $fixture; } + $uninflected = $name; // Tweak subdirectory names, so camelize() can make the correct name if (strpos($name, '/') > 0) { - $name = str_replace('/', '\\ ', $name); + $name = str_replace('/', '\\', $name); + $uninflected = $name; + $name = str_replace('\\', '\ ', $name); } $name = Inflector::camelize($name); + if ($name !== $uninflected) { + deprecationWarning(sprintf( + 'Declaring fixtures in underscored format in TestCase::$fixtures is deprecated.' . "\n" . + 'Found "%s.%s" in "%s". Expected "%s.%s" instead.', + $type, + $uninflected, + get_class($test), + $type, + str_replace('\\', '/', $name) + )); + } + $nameSegments = [ $baseNamespace, 'Test\Fixture', @@ -235,7 +259,7 @@ protected function _loadFixtures($test) * * @param \Cake\Datasource\FixtureInterface $fixture the fixture object to create * @param \Cake\Database\Connection $db The Connection object instance to use - * @param array $sources The existing tables in the datasource. + * @param string[] $sources The existing tables in the datasource. * @param bool $drop whether drop the fixture if it is already created or not * @return void */ @@ -270,6 +294,7 @@ protected function _setupTable($fixture, $db, array $sources, $drop = true) * @param \Cake\TestSuite\TestCase $test The test to inspect for fixture loading. * @return void * @throws \Cake\Core\Exception\Exception When fixture records cannot be inserted. + * @throws \RuntimeException */ public function load($test) { @@ -360,7 +385,7 @@ public function load($test) /** * Run a function on each connection and collection of fixtures. * - * @param array $fixtures A list of fixtures to operate on. + * @param string[] $fixtures A list of fixtures to operate on. * @param callable $operation The operation to run on each connection + fixture set. * @return void */ @@ -369,9 +394,19 @@ protected function _runOperation($fixtures, $operation) $dbs = $this->_fixtureConnections($fixtures); foreach ($dbs as $connection => $fixtures) { $db = ConnectionManager::get($connection); - $logQueries = $db->logQueries(); + $newMethods = method_exists($db, 'isQueryLoggingEnabled'); + if ($newMethods) { + $logQueries = $db->isQueryLoggingEnabled(); + } else { + $logQueries = $db->logQueries(); + } + if ($logQueries && !$this->_debug) { - $db->logQueries(false); + if ($newMethods) { + $db->disableQueryLogging(); + } else { + $db->logQueries(false); + } } $db->transactional(function ($db) use ($fixtures, $operation) { $db->disableConstraints(function ($db) use ($fixtures, $operation) { @@ -379,7 +414,11 @@ protected function _runOperation($fixtures, $operation) }); }); if ($logQueries) { - $db->logQueries(true); + if ($newMethods) { + $db->enableQueryLogging(true); + } else { + $db->logQueries(true); + } } } } @@ -387,16 +426,16 @@ protected function _runOperation($fixtures, $operation) /** * Get the unique list of connections that a set of fixtures contains. * - * @param array $fixtures The array of fixtures a list of connections is needed from. + * @param string[] $fixtures The array of fixtures a list of connections is needed from. * @return array An array of connection names. */ protected function _fixtureConnections($fixtures) { $dbs = []; - foreach ($fixtures as $f) { - if (!empty($this->_loaded[$f])) { - $fixture = $this->_loaded[$f]; - $dbs[$fixture->connection()][$f] = $fixture; + foreach ($fixtures as $name) { + if (!empty($this->_loaded[$name])) { + $fixture = $this->_loaded[$name]; + $dbs[$fixture->connection()][$name] = $fixture; } } diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/IntegrationTestCase.php b/app/vendor/cakephp/cakephp/src/TestSuite/IntegrationTestCase.php index f19f7a670..af3d0ce74 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/IntegrationTestCase.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/IntegrationTestCase.php @@ -20,21 +20,6 @@ class_alias('PHPUnit_Exception', 'PHPUnit\Exception'); } -use Cake\Core\Configure; -use Cake\Database\Exception as DatabaseException; -use Cake\Http\ServerRequest; -use Cake\Http\Session; -use Cake\Routing\Router; -use Cake\TestSuite\Stub\TestExceptionRenderer; -use Cake\Utility\CookieCryptTrait; -use Cake\Utility\Hash; -use Cake\Utility\Security; -use Cake\Utility\Text; -use Cake\View\Helper\SecureFieldTokenTrait; -use Exception; -use LogicException; -use PHPUnit\Exception as PhpunitException; - /** * A test case class intended to make integration tests of * your controllers easier. @@ -44,1177 +29,10 @@ class_alias('PHPUnit_Exception', 'PHPUnit\Exception'); * It favours full integration tests over mock objects as you can test * more of your code easily and avoid some of the maintenance pitfalls * that mock objects create. + * + * @deprecated 3.7.0 Use Cake\TestSuite\IntegrationTestTrait instead */ abstract class IntegrationTestCase extends TestCase { - use CookieCryptTrait; - use SecureFieldTokenTrait; - - /** - * Track whether or not tests are run against - * the PSR7 HTTP stack. - * - * @var bool - */ - protected $_useHttpServer = false; - - /** - * The customized application class name. - * - * @var string|null - */ - protected $_appClass; - - /** - * The customized application constructor arguments. - * - * @var array|null - */ - protected $_appArgs; - - /** - * The data used to build the next request. - * - * @var array - */ - protected $_request = []; - - /** - * The response for the most recent request. - * - * @var \Cake\Http\Response|null - */ - protected $_response; - - /** - * The exception being thrown if the case. - * - * @var \Exception|null - */ - protected $_exception; - - /** - * Session data to use in the next request. - * - * @var array - */ - protected $_session = []; - - /** - * Cookie data to use in the next request. - * - * @var array - */ - protected $_cookie = []; - - /** - * The controller used in the last request. - * - * @var \Cake\Controller\Controller|null - */ - protected $_controller; - - /** - * The last rendered view - * - * @var string|null - */ - protected $_viewName; - - /** - * The last rendered layout - * - * @var string|null - */ - protected $_layoutName; - - /** - * The session instance from the last request - * - * @var \Cake\Http\Session|null - */ - protected $_requestSession; - - /** - * Boolean flag for whether or not the request should have - * a SecurityComponent token added. - * - * @var bool - */ - protected $_securityToken = false; - - /** - * Boolean flag for whether or not the request should have - * a CSRF token added. - * - * @var bool - */ - protected $_csrfToken = false; - - /** - * Boolean flag for whether or not the request should re-store - * flash messages - * - * @var bool - */ - protected $_retainFlashMessages = false; - - /** - * Stored flash messages before render - * - * @var null|array - */ - protected $_flashMessages; - - /** - * - * @var null|string - */ - protected $_cookieEncryptionKey; - - /** - * Auto-detect if the HTTP middleware stack should be used. - * - * @return void - */ - public function setUp() - { - parent::setUp(); - $namespace = Configure::read('App.namespace'); - $this->_useHttpServer = class_exists($namespace . '\Application'); - } - - /** - * Clears the state used for requests. - * - * @return void - */ - public function tearDown() - { - parent::tearDown(); - $this->_request = []; - $this->_session = []; - $this->_cookie = []; - $this->_response = null; - $this->_exception = null; - $this->_controller = null; - $this->_viewName = null; - $this->_layoutName = null; - $this->_requestSession = null; - $this->_appClass = null; - $this->_appArgs = null; - $this->_securityToken = false; - $this->_csrfToken = false; - $this->_retainFlashMessages = false; - $this->_useHttpServer = false; - } - - /** - * Toggle whether or not you want to use the HTTP Server stack. - * - * @param bool $enable Enable/disable the usage of the HTTP Stack. - * @return void - */ - public function useHttpServer($enable) - { - $this->_useHttpServer = (bool)$enable; - } - - /** - * Configure the application class to use in integration tests. - * - * Combined with `useHttpServer()` to customize the class name and constructor arguments - * of your application class. - * - * @param string $class The application class name. - * @param array|null $constructorArgs The constructor arguments for your application class. - * @return void - */ - public function configApplication($class, $constructorArgs) - { - $this->_appClass = $class; - $this->_appArgs = $constructorArgs; - } - - /** - * Calling this method will enable a SecurityComponent - * compatible token to be added to request data. This - * lets you easily test actions protected by SecurityComponent. - * - * @return void - */ - public function enableSecurityToken() - { - $this->_securityToken = true; - } - - /** - * Calling this method will add a CSRF token to the request. - * - * Both the POST data and cookie will be populated when this option - * is enabled. The default parameter names will be used. - * - * @return void - */ - public function enableCsrfToken() - { - $this->_csrfToken = true; - } - - /** - * Calling this method will re-store flash messages into the test session - * after being removed by the FlashHelper - * - * @return void - */ - public function enableRetainFlashMessages() - { - $this->_retainFlashMessages = true; - } - - /** - * Configures the data for the *next* request. - * - * This data is cleared in the tearDown() method. - * - * You can call this method multiple times to append into - * the current state. - * - * @param array $data The request data to use. - * @return void - */ - public function configRequest(array $data) - { - $this->_request = $data + $this->_request; - } - - /** - * Sets session data. - * - * This method lets you configure the session data - * you want to be used for requests that follow. The session - * state is reset in each tearDown(). - * - * You can call this method multiple times to append into - * the current state. - * - * @param array $data The session data to use. - * @return void - */ - public function session(array $data) - { - $this->_session = $data + $this->_session; - } - - /** - * Sets a request cookie for future requests. - * - * This method lets you configure the session data - * you want to be used for requests that follow. The session - * state is reset in each tearDown(). - * - * You can call this method multiple times to append into - * the current state. - * - * @param string $name The cookie name to use. - * @param mixed $value The value of the cookie. - * @return void - */ - public function cookie($name, $value) - { - $this->_cookie[$name] = $value; - } - - /** - * Returns the encryption key to be used. - * - * @return string - */ - protected function _getCookieEncryptionKey() - { - if (isset($this->_cookieEncryptionKey)) { - return $this->_cookieEncryptionKey; - } - - return Security::getSalt(); - } - - /** - * Sets a encrypted request cookie for future requests. - * - * The difference from cookie() is this encrypts the cookie - * value like the CookieComponent. - * - * @param string $name The cookie name to use. - * @param mixed $value The value of the cookie. - * @param string|bool $encrypt Encryption mode to use. - * @param string|null $key Encryption key used. Defaults - * to Security.salt. - * @return void - * @see \Cake\Utility\CookieCryptTrait::_encrypt() - */ - public function cookieEncrypted($name, $value, $encrypt = 'aes', $key = null) - { - $this->_cookieEncryptionKey = $key; - $this->_cookie[$name] = $this->_encrypt($value, $encrypt); - } - - /** - * Performs a GET request using the current request data. - * - * The response of the dispatched request will be stored as - * a property. You can use various assert methods to check the - * response. - * - * @param string|array $url The URL to request. - * @return void - * @throws \PHPUnit\Exception - */ - public function get($url) - { - $this->_sendRequest($url, 'GET'); - } - - /** - * Performs a POST request using the current request data. - * - * The response of the dispatched request will be stored as - * a property. You can use various assert methods to check the - * response. - * - * @param string|array $url The URL to request. - * @param array $data The data for the request. - * @return void - * @throws \PHPUnit\Exception - */ - public function post($url, $data = []) - { - $this->_sendRequest($url, 'POST', $data); - } - - /** - * Performs a PATCH request using the current request data. - * - * The response of the dispatched request will be stored as - * a property. You can use various assert methods to check the - * response. - * - * @param string|array $url The URL to request. - * @param array $data The data for the request. - * @return void - * @throws \PHPUnit\Exception - */ - public function patch($url, $data = []) - { - $this->_sendRequest($url, 'PATCH', $data); - } - - /** - * Performs a PUT request using the current request data. - * - * The response of the dispatched request will be stored as - * a property. You can use various assert methods to check the - * response. - * - * @param string|array $url The URL to request. - * @param array $data The data for the request. - * @return void - * @throws \PHPUnit\Exception - */ - public function put($url, $data = []) - { - $this->_sendRequest($url, 'PUT', $data); - } - - /** - * Performs a DELETE request using the current request data. - * - * The response of the dispatched request will be stored as - * a property. You can use various assert methods to check the - * response. - * - * @param string|array $url The URL to request. - * @return void - * @throws \PHPUnit\Exception - */ - public function delete($url) - { - $this->_sendRequest($url, 'DELETE'); - } - - /** - * Performs a HEAD request using the current request data. - * - * The response of the dispatched request will be stored as - * a property. You can use various assert methods to check the - * response. - * - * @param string|array $url The URL to request. - * @return void - * @throws \PHPUnit\Exception - */ - public function head($url) - { - $this->_sendRequest($url, 'HEAD'); - } - - /** - * Performs an OPTIONS request using the current request data. - * - * The response of the dispatched request will be stored as - * a property. You can use various assert methods to check the - * response. - * - * @param string|array $url The URL to request. - * @return void - * @throws \PHPUnit\Exception - */ - public function options($url) - { - $this->_sendRequest($url, 'OPTIONS'); - } - - /** - * Creates and send the request into a Dispatcher instance. - * - * Receives and stores the response for future inspection. - * - * @param string|array $url The URL - * @param string $method The HTTP method - * @param array|null $data The request data. - * @return void - * @throws \PHPUnit\Exception - */ - protected function _sendRequest($url, $method, $data = []) - { - $dispatcher = $this->_makeDispatcher(); - $url = $dispatcher->resolveUrl($url); - - try { - $request = $this->_buildRequest($url, $method, $data); - $response = $dispatcher->execute($request); - $this->_requestSession = $request['session']; - if ($this->_retainFlashMessages && $this->_flashMessages) { - $this->_requestSession->write('Flash', $this->_flashMessages); - } - $this->_response = $response; - } catch (PhpUnitException $e) { - throw $e; - } catch (DatabaseException $e) { - throw $e; - } catch (LogicException $e) { - throw $e; - } catch (Exception $e) { - $this->_exception = $e; - $this->_handleError($e); - } - } - - /** - * Get the correct dispatcher instance. - * - * @return \Cake\TestSuite\MiddlewareDispatcher|\Cake\TestSuite\LegacyRequestDispatcher A dispatcher instance - */ - protected function _makeDispatcher() - { - if ($this->_useHttpServer) { - return new MiddlewareDispatcher($this, $this->_appClass, $this->_appArgs); - } - - return new LegacyRequestDispatcher($this); - } - - /** - * Adds additional event spies to the controller/view event manager. - * - * @param \Cake\Event\Event $event A dispatcher event. - * @param \Cake\Controller\Controller|null $controller Controller instance. - * @return void - */ - public function controllerSpy($event, $controller = null) - { - if (!$controller) { - /** @var \Cake\Controller\Controller $controller */ - $controller = $event->getSubject(); - } - $this->_controller = $controller; - $events = $controller->getEventManager(); - $events->on('View.beforeRender', function ($event, $viewFile) use ($controller) { - if (!$this->_viewName) { - $this->_viewName = $viewFile; - } - if ($this->_retainFlashMessages) { - $this->_flashMessages = $controller->getRequest()->getSession()->read('Flash'); - } - }); - $events->on('View.beforeLayout', function ($event, $viewFile) { - $this->_layoutName = $viewFile; - }); - } - - /** - * Attempts to render an error response for a given exception. - * - * This method will attempt to use the configured exception renderer. - * If that class does not exist, the built-in renderer will be used. - * - * @param \Exception $exception Exception to handle. - * @return void - * @throws \Exception - */ - protected function _handleError($exception) - { - $class = Configure::read('Error.exceptionRenderer'); - if (empty($class) || !class_exists($class)) { - $class = 'Cake\Error\ExceptionRenderer'; - } - /** @var \Cake\Error\ExceptionRenderer $instance */ - $instance = new $class($exception); - $this->_response = $instance->render(); - } - - /** - * Creates a request object with the configured options and parameters. - * - * @param string|array $url The URL - * @param string $method The HTTP method - * @param array|null $data The request data. - * @return array The request context - */ - protected function _buildRequest($url, $method, $data) - { - $sessionConfig = (array)Configure::read('Session') + [ - 'defaults' => 'php', - ]; - $session = Session::create($sessionConfig); - $session->write($this->_session); - list ($url, $query) = $this->_url($url); - $tokenUrl = $url; - - if ($query) { - $tokenUrl .= '?' . $query; - } - - parse_str($query, $queryData); - $props = [ - 'url' => $url, - 'session' => $session, - 'query' => $queryData - ]; - if (is_string($data)) { - $props['input'] = $data; - } - if (!isset($props['input'])) { - $data = $this->_addTokens($tokenUrl, $data); - $props['post'] = $this->_castToString($data); - } - $props['cookies'] = $this->_cookie; - - $env = [ - 'REQUEST_METHOD' => $method, - 'QUERY_STRING' => $query, - 'REQUEST_URI' => $url, - ]; - if (isset($this->_request['headers'])) { - foreach ($this->_request['headers'] as $k => $v) { - $name = strtoupper(str_replace('-', '_', $k)); - if (!in_array($name, ['CONTENT_LENGTH', 'CONTENT_TYPE'])) { - $name = 'HTTP_' . $name; - } - $env[$name] = $v; - } - unset($this->_request['headers']); - } - $props['environment'] = $env; - $props = Hash::merge($props, $this->_request); - - return $props; - } - - /** - * Add the CSRF and Security Component tokens if necessary. - * - * @param string $url The URL the form is being submitted on. - * @param array $data The request body data. - * @return array The request body with tokens added. - */ - protected function _addTokens($url, $data) - { - if ($this->_securityToken === true) { - $keys = array_map(function ($field) { - return preg_replace('/(\.\d+)+$/', '', $field); - }, array_keys(Hash::flatten($data))); - $tokenData = $this->_buildFieldToken($url, array_unique($keys)); - $data['_Token'] = $tokenData; - $data['_Token']['debug'] = 'SecurityComponent debug data would be added here'; - } - - if ($this->_csrfToken === true) { - if (!isset($this->_cookie['csrfToken'])) { - $this->_cookie['csrfToken'] = Text::uuid(); - } - if (!isset($data['_csrfToken'])) { - $data['_csrfToken'] = $this->_cookie['csrfToken']; - } - } - - return $data; - } - - /** - * Recursively casts all data to string as that is how data would be POSTed in - * the real world - * - * @param array $data POST data - * @return array - */ - protected function _castToString($data) - { - foreach ($data as $key => $value) { - if (is_scalar($value)) { - $data[$key] = $value === false ? '0' : (string)$value; - - continue; - } - - if (is_array($value)) { - $looksLikeFile = isset($value['error'], $value['tmp_name'], $value['size']); - if ($looksLikeFile) { - continue; - } - - $data[$key] = $this->_castToString($value); - } - } - - return $data; - } - - /** - * Creates a valid request url and parameter array more like Request::_url() - * - * @param string $url The URL - * @return array Qualified URL and the query parameters - */ - protected function _url($url) - { - // re-create URL in ServerRequest's context so - // query strings are encoded as expected - $request = new ServerRequest(['url' => $url]); - $url = $request->getRequestTarget(); - - $query = ''; - - $path = parse_url($url, PHP_URL_PATH); - if (strpos($url, '?') !== false) { - $query = parse_url($url, PHP_URL_QUERY); - } - - return [$path, $query]; - } - - /** - * Get the response body as string - * - * @return string The response body. - */ - protected function _getBodyAsString() - { - return (string)$this->_response->getBody(); - } - - /** - * Fetches a view variable by name. - * - * If the view variable does not exist, null will be returned. - * - * @param string $name The view variable to get. - * @return mixed The view variable if set. - */ - public function viewVariable($name) - { - if (empty($this->_controller->viewVars)) { - $this->fail('There are no view variables, perhaps you need to run a request?'); - } - if (isset($this->_controller->viewVars[$name])) { - return $this->_controller->viewVars[$name]; - } - - return null; - } - - /** - * Asserts that the response status code is in the 2xx range. - * - * @param string $message Custom message for failure. - * @return void - */ - public function assertResponseOk($message = null) - { - if (empty($message)) { - $message = 'Status code is not between 200 and 204'; - } - $this->_assertStatus(200, 204, $message); - } - - /** - * Asserts that the response status code is in the 2xx/3xx range. - * - * @param string $message Custom message for failure. - * @return void - */ - public function assertResponseSuccess($message = null) - { - if (empty($message)) { - $message = 'Status code is not between 200 and 308'; - } - $this->_assertStatus(200, 308, $message); - } - - /** - * Asserts that the response status code is in the 4xx range. - * - * @param string $message Custom message for failure. - * @return void - */ - public function assertResponseError($message = null) - { - if (empty($message)) { - $message = 'Status code is not between 400 and 429'; - } - $this->_assertStatus(400, 429, $message); - } - - /** - * Asserts that the response status code is in the 5xx range. - * - * @param string $message Custom message for failure. - * @return void - */ - public function assertResponseFailure($message = null) - { - if (empty($message)) { - $message = 'Status code is not between 500 and 505'; - } - $this->_assertStatus(500, 505, $message); - } - - /** - * Asserts a specific response status code. - * - * @param int $code Status code to assert. - * @param string $message Custom message for failure. - * @return void - */ - public function assertResponseCode($code, $message = null) - { - $actual = $this->_response->getStatusCode(); - - if (empty($message)) { - $message = 'Status code is not ' . $code . ' but ' . $actual; - } - - $this->_assertStatus($code, $code, $message); - } - - /** - * Helper method for status assertions. - * - * @param int $min Min status code. - * @param int $max Max status code. - * @param string $message The error message. - * @return void - */ - protected function _assertStatus($min, $max, $message) - { - if (!$this->_response) { - $this->fail('No response set, cannot assert status code.'); - } - $status = $this->_response->getStatusCode(); - - if ($this->_exception && ($status < $min || $status > $max)) { - $this->fail($this->_exception->getMessage()); - } - - $this->assertGreaterThanOrEqual($min, $status, $message); - $this->assertLessThanOrEqual($max, $status, $message); - } - - /** - * Asserts that the Location header is correct. - * - * @param string|array|null $url The URL you expected the client to go to. This - * can either be a string URL or an array compatible with Router::url(). Use null to - * simply check for the existence of this header. - * @param string $message The failure message that will be appended to the generated message. - * @return void - */ - public function assertRedirect($url = null, $message = '') - { - if (!$this->_response) { - $this->fail('No response set, cannot assert location header. ' . $message); - } - $result = $this->_response->getHeaderLine('Location'); - if ($url === null) { - $this->assertNotEmpty($result, $message); - - return; - } - if (empty($result)) { - $this->fail('No location header set. ' . $message); - } - $this->assertEquals(Router::url($url, ['_full' => true]), $result, $message); - } - - /** - * Asserts that the Location header contains a substring - * - * @param string $url The URL you expected the client to go to. - * @param string $message The failure message that will be appended to the generated message. - * @return void - */ - public function assertRedirectContains($url, $message = '') - { - if (!$this->_response) { - $this->fail('No response set, cannot assert location header. ' . $message); - } - $result = $this->_response->getHeaderLine('Location'); - if (empty($result)) { - $this->fail('No location header set. ' . $message); - } - $this->assertContains($url, $result, $message); - } - - /** - * Asserts that the Location header is not set. - * - * @param string $message The failure message that will be appended to the generated message. - * @return void - */ - public function assertNoRedirect($message = '') - { - if (!$this->_response) { - $this->fail('No response set, cannot assert location header. ' . $message); - } - $result = $this->_response->getHeaderLine('Location'); - if (!$message) { - $message = 'Redirect header set'; - } - if (!empty($result)) { - $message .= ': ' . $result; - } - $this->assertEmpty($result, $message); - } - - /** - * Asserts response headers - * - * @param string $header The header to check - * @param string $content The content to check for. - * @param string $message The failure message that will be appended to the generated message. - * @return void - */ - public function assertHeader($header, $content, $message = '') - { - if (!$this->_response) { - $this->fail('No response set, cannot assert headers. ' . $message); - } - if (!$this->_response->hasHeader($header)) { - $this->fail("The '$header' header is not set. " . $message); - } - $actual = $this->_response->getHeaderLine($header); - $this->assertEquals($content, $actual, $message); - } - - /** - * Asserts response header contains a string - * - * @param string $header The header to check - * @param string $content The content to check for. - * @param string $message The failure message that will be appended to the generated message. - * @return void - */ - public function assertHeaderContains($header, $content, $message = '') - { - if (!$this->_response) { - $this->fail('No response set, cannot assert headers. ' . $message); - } - if (!$this->_response->hasHeader($header)) { - $this->fail("The '$header' header is not set. " . $message); - } - $actual = $this->_response->getHeaderLine($header); - $this->assertContains($content, $actual, $message); - } - - /** - * Asserts content type - * - * @param string $type The content-type to check for. - * @param string $message The failure message that will be appended to the generated message. - * @return void - */ - public function assertContentType($type, $message = '') - { - if (!$this->_response) { - $this->fail('No response set, cannot assert content-type. ' . $message); - } - $alias = $this->_response->getMimeType($type); - if ($alias !== false) { - $type = $alias; - } - $result = $this->_response->getType(); - $this->assertEquals($type, $result, $message); - } - - /** - * Asserts content exists in the response body. - * - * @param mixed $content The content to check for. - * @param string $message The failure message that will be appended to the generated message. - * @return void - */ - public function assertResponseEquals($content, $message = '') - { - if (!$this->_response) { - $this->fail('No response set, cannot assert content. ' . $message); - } - $this->assertEquals($content, $this->_getBodyAsString(), $message); - } - - /** - * Asserts content exists in the response body. - * - * @param string $content The content to check for. - * @param string $message The failure message that will be appended to the generated message. - * @param bool $ignoreCase A flag to check whether we should ignore case or not. - * @return void - */ - public function assertResponseContains($content, $message = '', $ignoreCase = false) - { - if (!$this->_response) { - $this->fail('No response set, cannot assert content. ' . $message); - } - $this->assertContains($content, $this->_getBodyAsString(), $message, $ignoreCase); - } - - /** - * Asserts content does not exist in the response body. - * - * @param string $content The content to check for. - * @param string $message The failure message that will be appended to the generated message. - * @return void - */ - public function assertResponseNotContains($content, $message = '') - { - if (!$this->_response) { - $this->fail('No response set, cannot assert content. ' . $message); - } - $this->assertNotContains($content, $this->_getBodyAsString(), $message); - } - - /** - * Asserts that the response body matches a given regular expression. - * - * @param string $pattern The pattern to compare against. - * @param string $message The failure message that will be appended to the generated message. - * @return void - */ - public function assertResponseRegExp($pattern, $message = '') - { - if (!$this->_response) { - $this->fail('No response set, cannot assert content. ' . $message); - } - $this->assertRegExp($pattern, $this->_getBodyAsString(), $message); - } - - /** - * Asserts that the response body does not match a given regular expression. - * - * @param string $pattern The pattern to compare against. - * @param string $message The failure message that will be appended to the generated message. - * @return void - */ - public function assertResponseNotRegExp($pattern, $message = '') - { - if (!$this->_response) { - $this->fail('No response set, cannot assert content. ' . $message); - } - $this->assertNotRegExp($pattern, $this->_getBodyAsString(), $message); - } - - /** - * Assert response content is not empty. - * - * @param string $message The failure message that will be appended to the generated message. - * @return void - */ - public function assertResponseNotEmpty($message = '') - { - if (!$this->_response) { - $this->fail('No response set, cannot assert content. ' . $message); - } - $this->assertNotEmpty($this->_getBodyAsString(), $message); - } - /** - * Assert response content is empty. - * - * @param string $message The failure message that will be appended to the generated message. - * @return void - */ - public function assertResponseEmpty($message = '') - { - if (!$this->_response) { - $this->fail('No response set, cannot assert content. ' . $message); - } - $this->assertEmpty($this->_getBodyAsString(), $message); - } - - /** - * Asserts that the search string was in the template name. - * - * @param string $content The content to check for. - * @param string $message The failure message that will be appended to the generated message. - * @return void - */ - public function assertTemplate($content, $message = '') - { - if (!$this->_viewName) { - $this->fail('No view name stored. ' . $message); - } - $this->assertContains($content, $this->_viewName, $message); - } - - /** - * Asserts that the search string was in the layout name. - * - * @param string $content The content to check for. - * @param string $message The failure message that will be appended to the generated message. - * @return void - */ - public function assertLayout($content, $message = '') - { - if (!$this->_layoutName) { - $this->fail('No layout name stored. ' . $message); - } - $this->assertContains($content, $this->_layoutName, $message); - } - - /** - * Asserts session contents - * - * @param string $expected The expected contents. - * @param string $path The session data path. Uses Hash::get() compatible notation - * @param string $message The failure message that will be appended to the generated message. - * @return void - */ - public function assertSession($expected, $path, $message = '') - { - if (empty($this->_requestSession)) { - $this->fail('There is no stored session data. Perhaps you need to run a request?'); - } - $result = $this->_requestSession->read($path); - $this->assertEquals( - $expected, - $result, - 'Session content for "' . $path . '" differs. ' . $message - ); - } - - /** - * Asserts cookie values - * - * @param string $expected The expected contents. - * @param string $name The cookie name. - * @param string $message The failure message that will be appended to the generated message. - * @return void - */ - public function assertCookie($expected, $name, $message = '') - { - if (!$this->_response) { - $this->fail('Not response set, cannot assert cookies.'); - } - $result = $this->_response->getCookie($name); - $this->assertEquals( - $expected, - $result['value'], - 'Cookie "' . $name . '" data differs. ' . $message - ); - } - - /** - * Asserts a cookie has not been set in the response - * - * @param string $cookie The cookie name to check - * @param string $message The failure message that will be appended to the generated message. - * @return void - */ - public function assertCookieNotSet($cookie, $message = '') - { - if (!$this->_response) { - $this->fail('No response set, cannot assert cookies. ' . $message); - } - - $this->assertCookie(null, $cookie, "Cookie '{$cookie}' has been set. " . $message); - } - - /** - * Disable the error handler middleware. - * - * By using this function, exceptions are no longer caught by the ErrorHandlerMiddleware - * and are instead re-thrown by the TestExceptionRenderer. This can be helpful - * when trying to diagnose/debug unexpected failures in test cases. - * - * @return void - */ - public function disableErrorHandlerMiddleware() - { - Configure::write('Error.exceptionRenderer', TestExceptionRenderer::class); - } - - /** - * Asserts cookie values which are encrypted by the - * CookieComponent. - * - * The difference from assertCookie() is this decrypts the cookie - * value like the CookieComponent for this assertion. - * - * @param string $expected The expected contents. - * @param string $name The cookie name. - * @param string|bool $encrypt Encryption mode to use. - * @param string|null $key Encryption key used. Defaults - * to Security.salt. - * @param string $message The failure message that will be appended to the generated message. - * @return void - * @see \Cake\Utility\CookieCryptTrait::_encrypt() - */ - public function assertCookieEncrypted($expected, $name, $encrypt = 'aes', $key = null, $message = '') - { - if (!$this->_response) { - $this->fail('No response set, cannot assert cookies.'); - } - $result = $this->_response->getCookie($name); - $this->_cookieEncryptionKey = $key; - $result['value'] = $this->_decrypt($result['value'], $encrypt); - $this->assertEquals($expected, $result['value'], 'Cookie data differs. ' . $message); - } - - /** - * Asserts that a file with the given name was sent in the response - * - * @param string $expected The file name that should be sent in the response - * @param string $message The failure message that will be appended to the generated message. - * @return void - */ - public function assertFileResponse($expected, $message = '') - { - if ($this->_response === null) { - $this->fail('No response set, cannot assert file.'); - } - $actual = isset($this->_response->getFile()->path) ? $this->_response->getFile()->path : null; - - if ($actual === null) { - $this->fail('No file was sent in this response'); - } - $this->assertEquals($expected, $actual, $message); - } + use IntegrationTestTrait; } diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/IntegrationTestTrait.php b/app/vendor/cakephp/cakephp/src/TestSuite/IntegrationTestTrait.php new file mode 100644 index 000000000..a339d7d14 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/IntegrationTestTrait.php @@ -0,0 +1,1293 @@ +_useHttpServer = class_exists($namespace . '\Application'); + } + + /** + * Clears the state used for requests. + * + * @after + * @return void + */ + public function cleanup() + { + $this->_request = []; + $this->_session = []; + $this->_cookie = []; + $this->_response = null; + $this->_exception = null; + $this->_controller = null; + $this->_viewName = null; + $this->_layoutName = null; + $this->_requestSession = null; + $this->_appClass = null; + $this->_appArgs = null; + $this->_securityToken = false; + $this->_csrfToken = false; + $this->_retainFlashMessages = false; + $this->_useHttpServer = false; + } + + /** + * Toggle whether or not you want to use the HTTP Server stack. + * + * @param bool $enable Enable/disable the usage of the HTTP Stack. + * @return void + */ + public function useHttpServer($enable) + { + $this->_useHttpServer = (bool)$enable; + } + + /** + * Configure the application class to use in integration tests. + * + * Combined with `useHttpServer()` to customize the class name and constructor arguments + * of your application class. + * + * @param string $class The application class name. + * @param array|null $constructorArgs The constructor arguments for your application class. + * @return void + */ + public function configApplication($class, $constructorArgs) + { + $this->_appClass = $class; + $this->_appArgs = $constructorArgs; + } + + /** + * Calling this method will enable a SecurityComponent + * compatible token to be added to request data. This + * lets you easily test actions protected by SecurityComponent. + * + * @return void + */ + public function enableSecurityToken() + { + $this->_securityToken = true; + } + + /** + * Set list of fields that are excluded from field validation. + * + * @param string[] $unlockedFields List of fields that are excluded from field validation. + * @return void + */ + public function setUnlockedFields(array $unlockedFields = []) + { + $this->_unlockedFields = $unlockedFields; + } + + /** + * Calling this method will add a CSRF token to the request. + * + * Both the POST data and cookie will be populated when this option + * is enabled. The default parameter names will be used. + * + * @return void + */ + public function enableCsrfToken() + { + $this->_csrfToken = true; + } + + /** + * Calling this method will re-store flash messages into the test session + * after being removed by the FlashHelper + * + * @return void + */ + public function enableRetainFlashMessages() + { + $this->_retainFlashMessages = true; + } + + /** + * Configures the data for the *next* request. + * + * This data is cleared in the tearDown() method. + * + * You can call this method multiple times to append into + * the current state. + * + * @param array $data The request data to use. + * @return void + */ + public function configRequest(array $data) + { + $this->_request = $data + $this->_request; + } + + /** + * Sets session data. + * + * This method lets you configure the session data + * you want to be used for requests that follow. The session + * state is reset in each tearDown(). + * + * You can call this method multiple times to append into + * the current state. + * + * @param array $data The session data to use. + * @return void + */ + public function session(array $data) + { + $this->_session = $data + $this->_session; + } + + /** + * Sets a request cookie for future requests. + * + * This method lets you configure the session data + * you want to be used for requests that follow. The session + * state is reset in each tearDown(). + * + * You can call this method multiple times to append into + * the current state. + * + * @param string $name The cookie name to use. + * @param mixed $value The value of the cookie. + * @return void + */ + public function cookie($name, $value) + { + $this->_cookie[$name] = $value; + } + + /** + * Returns the encryption key to be used. + * + * @return string + */ + protected function _getCookieEncryptionKey() + { + if (isset($this->_cookieEncryptionKey)) { + return $this->_cookieEncryptionKey; + } + + return Security::getSalt(); + } + + /** + * Sets a encrypted request cookie for future requests. + * + * The difference from cookie() is this encrypts the cookie + * value like the CookieComponent. + * + * @param string $name The cookie name to use. + * @param mixed $value The value of the cookie. + * @param string|bool $encrypt Encryption mode to use. + * @param string|null $key Encryption key used. Defaults + * to Security.salt. + * @return void + * @see \Cake\Utility\CookieCryptTrait::_encrypt() + */ + public function cookieEncrypted($name, $value, $encrypt = 'aes', $key = null) + { + $this->_cookieEncryptionKey = $key; + $this->_cookie[$name] = $this->_encrypt($value, $encrypt); + } + + /** + * Performs a GET request using the current request data. + * + * The response of the dispatched request will be stored as + * a property. You can use various assert methods to check the + * response. + * + * @param string|array $url The URL to request. + * @return void + * @throws \PHPUnit\Exception + */ + public function get($url) + { + $this->_sendRequest($url, 'GET'); + } + + /** + * Performs a POST request using the current request data. + * + * The response of the dispatched request will be stored as + * a property. You can use various assert methods to check the + * response. + * + * @param string|array $url The URL to request. + * @param string|array|null $data The data for the request. + * @return void + * @throws \PHPUnit\Exception + */ + public function post($url, $data = []) + { + $this->_sendRequest($url, 'POST', $data); + } + + /** + * Performs a PATCH request using the current request data. + * + * The response of the dispatched request will be stored as + * a property. You can use various assert methods to check the + * response. + * + * @param string|array $url The URL to request. + * @param string|array|null $data The data for the request. + * @return void + * @throws \PHPUnit\Exception + */ + public function patch($url, $data = []) + { + $this->_sendRequest($url, 'PATCH', $data); + } + + /** + * Performs a PUT request using the current request data. + * + * The response of the dispatched request will be stored as + * a property. You can use various assert methods to check the + * response. + * + * @param string|array $url The URL to request. + * @param string|array|null $data The data for the request. + * @return void + * @throws \PHPUnit\Exception + */ + public function put($url, $data = []) + { + $this->_sendRequest($url, 'PUT', $data); + } + + /** + * Performs a DELETE request using the current request data. + * + * The response of the dispatched request will be stored as + * a property. You can use various assert methods to check the + * response. + * + * @param string|array $url The URL to request. + * @return void + * @throws \PHPUnit\Exception + */ + public function delete($url) + { + $this->_sendRequest($url, 'DELETE'); + } + + /** + * Performs a HEAD request using the current request data. + * + * The response of the dispatched request will be stored as + * a property. You can use various assert methods to check the + * response. + * + * @param string|array $url The URL to request. + * @return void + * @throws \PHPUnit\Exception + */ + public function head($url) + { + $this->_sendRequest($url, 'HEAD'); + } + + /** + * Performs an OPTIONS request using the current request data. + * + * The response of the dispatched request will be stored as + * a property. You can use various assert methods to check the + * response. + * + * @param string|array $url The URL to request. + * @return void + * @throws \PHPUnit\Exception + */ + public function options($url) + { + $this->_sendRequest($url, 'OPTIONS'); + } + + /** + * Creates and send the request into a Dispatcher instance. + * + * Receives and stores the response for future inspection. + * + * @param string|array $url The URL + * @param string $method The HTTP method + * @param string|array|null $data The request data. + * @return void + * @throws \PHPUnit\Exception + */ + protected function _sendRequest($url, $method, $data = []) + { + $dispatcher = $this->_makeDispatcher(); + $url = $dispatcher->resolveUrl($url); + + try { + $request = $this->_buildRequest($url, $method, $data); + $response = $dispatcher->execute($request); + $this->_requestSession = $request['session']; + if ($this->_retainFlashMessages && $this->_flashMessages) { + $this->_requestSession->write('Flash', $this->_flashMessages); + } + $this->_response = $response; + } catch (PhpUnitException $e) { + throw $e; + } catch (DatabaseException $e) { + throw $e; + } catch (LogicException $e) { + throw $e; + } catch (Exception $e) { + $this->_exception = $e; + // Simulate the global exception handler being invoked. + $this->_handleError($e); + } + } + + /** + * Get the correct dispatcher instance. + * + * @return \Cake\TestSuite\MiddlewareDispatcher|\Cake\TestSuite\LegacyRequestDispatcher A dispatcher instance + */ + protected function _makeDispatcher() + { + if ($this->_useHttpServer) { + return new MiddlewareDispatcher($this, $this->_appClass, $this->_appArgs); + } + + return new LegacyRequestDispatcher($this); + } + + /** + * Adds additional event spies to the controller/view event manager. + * + * @param \Cake\Event\Event $event A dispatcher event. + * @param \Cake\Controller\Controller|null $controller Controller instance. + * @return void + */ + public function controllerSpy($event, $controller = null) + { + if (!$controller) { + /** @var \Cake\Controller\Controller $controller */ + $controller = $event->getSubject(); + } + $this->_controller = $controller; + $events = $controller->getEventManager(); + $events->on('View.beforeRender', function ($event, $viewFile) use ($controller) { + if (!$this->_viewName) { + $this->_viewName = $viewFile; + } + if ($this->_retainFlashMessages) { + $this->_flashMessages = $controller->getRequest()->getSession()->read('Flash'); + } + }); + $events->on('View.beforeLayout', function ($event, $viewFile) { + $this->_layoutName = $viewFile; + }); + } + + /** + * Attempts to render an error response for a given exception. + * + * This method will attempt to use the configured exception renderer. + * If that class does not exist, the built-in renderer will be used. + * + * @param \Exception $exception Exception to handle. + * @return void + * @throws \Exception + */ + protected function _handleError($exception) + { + $class = Configure::read('Error.exceptionRenderer'); + if (empty($class) || !class_exists($class)) { + $class = 'Cake\Error\ExceptionRenderer'; + } + /** @var \Cake\Error\ExceptionRenderer $instance */ + $instance = new $class($exception); + $this->_response = $instance->render(); + } + + /** + * Creates a request object with the configured options and parameters. + * + * @param string|array $url The URL + * @param string $method The HTTP method + * @param string|array|null $data The request data. + * @return array The request context + */ + protected function _buildRequest($url, $method, $data) + { + $sessionConfig = (array)Configure::read('Session') + [ + 'defaults' => 'php', + ]; + $session = Session::create($sessionConfig); + $session->write($this->_session); + list($url, $query, $hostInfo) = $this->_url($url); + $tokenUrl = $url; + + if ($query) { + $tokenUrl .= '?' . $query; + } + + parse_str($query, $queryData); + $props = [ + 'url' => $url, + 'session' => $session, + 'query' => $queryData, + 'files' => [], + ]; + if (is_string($data)) { + $props['input'] = $data; + } + if (!isset($props['input'])) { + $data = $this->_addTokens($tokenUrl, $data); + $props['post'] = $this->_castToString($data); + } + $props['cookies'] = $this->_cookie; + + $env = [ + 'REQUEST_METHOD' => $method, + 'QUERY_STRING' => $query, + 'REQUEST_URI' => $url, + ]; + if (!empty($hostInfo['ssl'])) { + $env['HTTPS'] = 'on'; + } + if (isset($hostInfo['host'])) { + $env['HTTP_HOST'] = $hostInfo['host']; + } + if (isset($this->_request['headers'])) { + foreach ($this->_request['headers'] as $k => $v) { + $name = strtoupper(str_replace('-', '_', $k)); + if (!in_array($name, ['CONTENT_LENGTH', 'CONTENT_TYPE'])) { + $name = 'HTTP_' . $name; + } + $env[$name] = $v; + } + unset($this->_request['headers']); + } + $props['environment'] = $env; + $props = Hash::merge($props, $this->_request); + + return $props; + } + + /** + * Add the CSRF and Security Component tokens if necessary. + * + * @param string $url The URL the form is being submitted on. + * @param array $data The request body data. + * @return array The request body with tokens added. + */ + protected function _addTokens($url, $data) + { + if ($this->_securityToken === true) { + $fields = array_diff_key($data, array_flip($this->_unlockedFields)); + + $keys = array_map(function ($field) { + return preg_replace('/(\.\d+)+$/', '', $field); + }, array_keys(Hash::flatten($fields))); + + $tokenData = $this->_buildFieldToken($url, array_unique($keys), $this->_unlockedFields); + + $data['_Token'] = $tokenData; + $data['_Token']['debug'] = 'SecurityComponent debug data would be added here'; + } + + if ($this->_csrfToken === true) { + if (!isset($this->_cookie['csrfToken'])) { + $this->_cookie['csrfToken'] = Text::uuid(); + } + if (!isset($data['_csrfToken'])) { + $data['_csrfToken'] = $this->_cookie['csrfToken']; + } + } + + return $data; + } + + /** + * Recursively casts all data to string as that is how data would be POSTed in + * the real world + * + * @param array $data POST data + * @return array + */ + protected function _castToString($data) + { + foreach ($data as $key => $value) { + if (is_scalar($value)) { + $data[$key] = $value === false ? '0' : (string)$value; + + continue; + } + + if (is_array($value)) { + $looksLikeFile = isset($value['error'], $value['tmp_name'], $value['size']); + if ($looksLikeFile) { + continue; + } + + $data[$key] = $this->_castToString($value); + } + } + + return $data; + } + + /** + * Creates a valid request url and parameter array more like Request::_url() + * + * @param string|array $url The URL + * @return array Qualified URL, the query parameters, and host data + */ + protected function _url($url) + { + $uri = new Uri($url); + $path = $uri->getPath(); + $query = $uri->getQuery(); + + $hostData = []; + if ($uri->getHost()) { + $hostData['host'] = $uri->getHost(); + } + if ($uri->getScheme()) { + $hostData['ssl'] = $uri->getScheme() === 'https'; + } + + return [$path, $query, $hostData]; + } + + /** + * Get the response body as string + * + * @return string The response body. + */ + protected function _getBodyAsString() + { + if (!$this->_response) { + $this->fail('No response set, cannot assert content.'); + } + + return (string)$this->_response->getBody(); + } + + /** + * Fetches a view variable by name. + * + * If the view variable does not exist, null will be returned. + * + * @param string $name The view variable to get. + * @return mixed The view variable if set. + */ + public function viewVariable($name) + { + if (empty($this->_controller->viewVars)) { + $this->fail('There are no view variables, perhaps you need to run a request?'); + } + if (isset($this->_controller->viewVars[$name])) { + return $this->_controller->viewVars[$name]; + } + + return null; + } + + /** + * Asserts that the response status code is in the 2xx range. + * + * @param string $message Custom message for failure. + * @return void + */ + public function assertResponseOk($message = null) + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat(null, new StatusOk($this->_response), $verboseMessage); + } + + /** + * Asserts that the response status code is in the 2xx/3xx range. + * + * @param string $message Custom message for failure. + * @return void + */ + public function assertResponseSuccess($message = null) + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat(null, new StatusSuccess($this->_response), $verboseMessage); + } + + /** + * Asserts that the response status code is in the 4xx range. + * + * @param string $message Custom message for failure. + * @return void + */ + public function assertResponseError($message = null) + { + $this->assertThat(null, new StatusError($this->_response), $message); + } + + /** + * Asserts that the response status code is in the 5xx range. + * + * @param string $message Custom message for failure. + * @return void + */ + public function assertResponseFailure($message = null) + { + $this->assertThat(null, new StatusFailure($this->_response), $message); + } + + /** + * Asserts a specific response status code. + * + * @param int $code Status code to assert. + * @param string $message Custom message for failure. + * @return void + */ + public function assertResponseCode($code, $message = null) + { + $this->assertThat($code, new StatusCode($this->_response), $message); + } + + /** + * Asserts that the Location header is correct. + * + * @param string|array|null $url The URL you expected the client to go to. This + * can either be a string URL or an array compatible with Router::url(). Use null to + * simply check for the existence of this header. + * @param string $message The failure message that will be appended to the generated message. + * @return void + */ + public function assertRedirect($url = null, $message = '') + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat(null, new HeaderSet($this->_response, 'Location'), $verboseMessage); + + if ($url) { + $this->assertThat(Router::url($url, ['_full' => true]), new HeaderEquals($this->_response, 'Location'), $verboseMessage); + } + } + + /** + * Asserts that the Location header contains a substring + * + * @param string $url The URL you expected the client to go to. + * @param string $message The failure message that will be appended to the generated message. + * @return void + */ + public function assertRedirectContains($url, $message = '') + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat(null, new HeaderSet($this->_response, 'Location'), $verboseMessage); + $this->assertThat($url, new HeaderContains($this->_response, 'Location'), $verboseMessage); + } + + /** + * Asserts that the Location header does not contain a substring + * + * @param string $url The URL you expected the client to go to. + * @param string $message The failure message that will be appended to the generated message. + * @return void + */ + public function assertRedirectNotContains($url, $message = '') + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat(null, new HeaderSet($this->_response, 'Location'), $verboseMessage); + $this->assertThat($url, new HeaderNotContains($this->_response, 'Location'), $verboseMessage); + } + + /** + * Asserts that the Location header is not set. + * + * @param string $message The failure message that will be appended to the generated message. + * @return void + */ + public function assertNoRedirect($message = '') + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat(null, new HeaderNotSet($this->_response, 'Location'), $verboseMessage); + } + + /** + * Asserts response headers + * + * @param string $header The header to check + * @param string $content The content to check for. + * @param string $message The failure message that will be appended to the generated message. + * @return void + */ + public function assertHeader($header, $content, $message = '') + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat(null, new HeaderSet($this->_response, $header), $verboseMessage); + $this->assertThat($content, new HeaderEquals($this->_response, $header), $verboseMessage); + } + + /** + * Asserts response header contains a string + * + * @param string $header The header to check + * @param string $content The content to check for. + * @param string $message The failure message that will be appended to the generated message. + * @return void + */ + public function assertHeaderContains($header, $content, $message = '') + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat(null, new HeaderSet($this->_response, $header), $verboseMessage); + $this->assertThat($content, new HeaderContains($this->_response, $header), $verboseMessage); + } + + /** + * Asserts response header does not contain a string + * + * @param string $header The header to check + * @param string $content The content to check for. + * @param string $message The failure message that will be appended to the generated message. + * @return void + */ + public function assertHeaderNotContains($header, $content, $message = '') + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat(null, new HeaderSet($this->_response, $header), $verboseMessage); + $this->assertThat($content, new HeaderNotContains($this->_response, $header), $verboseMessage); + } + + /** + * Asserts content type + * + * @param string $type The content-type to check for. + * @param string $message The failure message that will be appended to the generated message. + * @return void + */ + public function assertContentType($type, $message = '') + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat($type, new ContentType($this->_response), $verboseMessage); + } + + /** + * Asserts content in the response body equals. + * + * @param mixed $content The content to check for. + * @param string $message The failure message that will be appended to the generated message. + * @return void + */ + public function assertResponseEquals($content, $message = '') + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat($content, new BodyEquals($this->_response), $verboseMessage); + } + + /** + * Asserts content in the response body not equals. + * + * @param mixed $content The content to check for. + * @param string $message The failure message that will be appended to the generated message. + * @return void + */ + public function assertResponseNotEquals($content, $message = '') + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat($content, new BodyNotEquals($this->_response), $verboseMessage); + } + + /** + * Asserts content exists in the response body. + * + * @param string $content The content to check for. + * @param string $message The failure message that will be appended to the generated message. + * @param bool $ignoreCase A flag to check whether we should ignore case or not. + * @return void + */ + public function assertResponseContains($content, $message = '', $ignoreCase = false) + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat($content, new BodyContains($this->_response, $ignoreCase), $verboseMessage); + } + + /** + * Asserts content does not exist in the response body. + * + * @param string $content The content to check for. + * @param string $message The failure message that will be appended to the generated message. + * @param bool $ignoreCase A flag to check whether we should ignore case or not. + * @return void + */ + public function assertResponseNotContains($content, $message = '', $ignoreCase = false) + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat($content, new BodyNotContains($this->_response, $ignoreCase), $verboseMessage); + } + + /** + * Asserts that the response body matches a given regular expression. + * + * @param string $pattern The pattern to compare against. + * @param string $message The failure message that will be appended to the generated message. + * @return void + */ + public function assertResponseRegExp($pattern, $message = '') + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat($pattern, new BodyRegExp($this->_response), $verboseMessage); + } + + /** + * Asserts that the response body does not match a given regular expression. + * + * @param string $pattern The pattern to compare against. + * @param string $message The failure message that will be appended to the generated message. + * @return void + */ + public function assertResponseNotRegExp($pattern, $message = '') + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat($pattern, new BodyNotRegExp($this->_response), $verboseMessage); + } + + /** + * Assert response content is not empty. + * + * @param string $message The failure message that will be appended to the generated message. + * @return void + */ + public function assertResponseNotEmpty($message = '') + { + $this->assertThat(null, new BodyNotEmpty($this->_response), $message); + } + + /** + * Assert response content is empty. + * + * @param string $message The failure message that will be appended to the generated message. + * @return void + */ + public function assertResponseEmpty($message = '') + { + $this->assertThat(null, new BodyEmpty($this->_response), $message); + } + + /** + * Asserts that the search string was in the template name. + * + * @param string $content The content to check for. + * @param string $message The failure message that will be appended to the generated message. + * @return void + */ + public function assertTemplate($content, $message = '') + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat($content, new TemplateFileEquals($this->_viewName), $verboseMessage); + } + + /** + * Asserts that the search string was in the layout name. + * + * @param string $content The content to check for. + * @param string $message The failure message that will be appended to the generated message. + * @return void + */ + public function assertLayout($content, $message = '') + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat($content, new LayoutFileEquals($this->_layoutName), $verboseMessage); + } + + /** + * Asserts session contents + * + * @param string $expected The expected contents. + * @param string $path The session data path. Uses Hash::get() compatible notation + * @param string $message The failure message that will be appended to the generated message. + * @return void + */ + public function assertSession($expected, $path, $message = '') + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat($expected, new SessionEquals($this->_requestSession, $path), $verboseMessage); + } + + /** + * Asserts a flash message was set + * + * @param string $expected Expected message + * @param string $key Flash key + * @param string $message Assertion failure message + * @return void + */ + public function assertFlashMessage($expected, $key = 'flash', $message = '') + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat($expected, new FlashParamEquals($this->_requestSession, $key, 'message'), $verboseMessage); + } + + /** + * Asserts a flash message was set at a certain index + * + * @param int $at Flash index + * @param string $expected Expected message + * @param string $key Flash key + * @param string $message Assertion failure message + * @return void + */ + public function assertFlashMessageAt($at, $expected, $key = 'flash', $message = '') + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat($expected, new FlashParamEquals($this->_requestSession, $key, 'message', $at), $verboseMessage); + } + + /** + * Asserts a flash element was set + * + * @param string $expected Expected element name + * @param string $key Flash key + * @param string $message Assertion failure message + * @return void + */ + public function assertFlashElement($expected, $key = 'flash', $message = '') + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat($expected, new FlashParamEquals($this->_requestSession, $key, 'element'), $verboseMessage); + } + + /** + * Asserts a flash element was set at a certain index + * + * @param int $at Flash index + * @param string $expected Expected element name + * @param string $key Flash key + * @param string $message Assertion failure message + * @return void + */ + public function assertFlashElementAt($at, $expected, $key = 'flash', $message = '') + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat($expected, new FlashParamEquals($this->_requestSession, $key, 'element', $at), $verboseMessage); + } + + /** + * Asserts cookie values + * + * @param string $expected The expected contents. + * @param string $name The cookie name. + * @param string $message The failure message that will be appended to the generated message. + * @return void + */ + public function assertCookie($expected, $name, $message = '') + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat($name, new CookieSet($this->_response), $verboseMessage); + $this->assertThat($expected, new CookieEquals($this->_response, $name), $verboseMessage); + } + + /** + * Asserts a cookie has not been set in the response + * + * @param string $cookie The cookie name to check + * @param string $message The failure message that will be appended to the generated message. + * @return void + */ + public function assertCookieNotSet($cookie, $message = '') + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat($cookie, new CookieNotSet($this->_response), $verboseMessage); + } + + /** + * Disable the error handler middleware. + * + * By using this function, exceptions are no longer caught by the ErrorHandlerMiddleware + * and are instead re-thrown by the TestExceptionRenderer. This can be helpful + * when trying to diagnose/debug unexpected failures in test cases. + * + * @return void + */ + public function disableErrorHandlerMiddleware() + { + Configure::write('Error.exceptionRenderer', TestExceptionRenderer::class); + } + + /** + * Asserts cookie values which are encrypted by the + * CookieComponent. + * + * The difference from assertCookie() is this decrypts the cookie + * value like the CookieComponent for this assertion. + * + * @param string $expected The expected contents. + * @param string $name The cookie name. + * @param string|bool $encrypt Encryption mode to use. + * @param string|null $key Encryption key used. Defaults + * to Security.salt. + * @param string $message The failure message that will be appended to the generated message. + * @return void + * @see \Cake\Utility\CookieCryptTrait::_encrypt() + */ + public function assertCookieEncrypted($expected, $name, $encrypt = 'aes', $key = null, $message = '') + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat($name, new CookieSet($this->_response), $verboseMessage); + + $this->_cookieEncryptionKey = $key; + $this->assertThat($expected, new CookieEncryptedEquals($this->_response, $name, $encrypt, $this->_getCookieEncryptionKey())); + } + + /** + * Asserts that a file with the given name was sent in the response + * + * @param string $expected The absolute file path that should be sent in the response. + * @param string $message The failure message that will be appended to the generated message. + * @return void + */ + public function assertFileResponse($expected, $message = '') + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat(null, new FileSent($this->_response), $verboseMessage); + $this->assertThat($expected, new FileSentAs($this->_response), $verboseMessage); + } + + /** + * Inspect controller to extract possible causes of the failed assertion + * + * @param string $message Original message to use as a base + * @return null|string + */ + protected function extractVerboseMessage($message = null) + { + if ($this->_exception instanceof \Exception) { + $message .= $this->extractExceptionMessage($this->_exception); + } + if ($this->_controller === null) { + return $message; + } + $error = Hash::get($this->_controller->viewVars, 'error'); + if ($error instanceof \Exception) { + $message .= $this->extractExceptionMessage($this->viewVariable('error')); + } + + return $message; + } + + /** + * Extract verbose message for existing exception + * + * @param \Exception $exception Exception to extract + * @return string + */ + protected function extractExceptionMessage(\Exception $exception) + { + return PHP_EOL . + sprintf('Possibly related to %s: "%s" ', get_class($exception), $exception->getMessage()) . + PHP_EOL . + $exception->getTraceAsString(); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/MiddlewareDispatcher.php b/app/vendor/cakephp/cakephp/src/TestSuite/MiddlewareDispatcher.php index 1b7dcc884..b2b0ae927 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/MiddlewareDispatcher.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/MiddlewareDispatcher.php @@ -68,8 +68,12 @@ class MiddlewareDispatcher * @param string|null $class The application class name. Defaults to App\Application. * @param array|null $constructorArgs The constructor arguments for your application class. * Defaults to `['./config']` + * @param bool $disableRouterReload Disable Router::reload() call when resolving URLs. This + * flag may be necessary if you are using Router methods in your test case setup, and using array URLs + * when doing requests in your tests. + * @throws \LogicException If it cannot load class for use in integration testing. */ - public function __construct($test, $class = null, $constructorArgs = null) + public function __construct($test, $class = null, $constructorArgs = null, $disableRouterReload = false) { $this->_test = $test; $this->_class = $class ?: Configure::read('App.namespace') . '\Application'; @@ -77,7 +81,9 @@ public function __construct($test, $class = null, $constructorArgs = null) try { $reflect = new ReflectionClass($this->_class); - $this->app = $reflect->newInstanceArgs($this->_constructorArgs); + /** @var \Cake\Core\HttpApplicationInterface $app */ + $app = $reflect->newInstanceArgs($this->_constructorArgs); + $this->app = $app; } catch (ReflectionException $e) { throw new LogicException(sprintf('Cannot load "%s" for use in integration testing.', $this->_class)); } @@ -123,37 +129,16 @@ protected function resolveRoute(array $url) } $out = Router::url($url); - Router::reload(); + Router::resetRoutes(); return $out; } - /** - * Run a request and get the response. - * - * @param \Cake\Http\ServerRequest $request The request to execute. - * @return \Psr\Http\Message\ResponseInterface The generated response. - */ - public function execute($request) - { - // Spy on the controller using the initialize hook instead - // of the dispatcher hooks as those will be going away one day. - EventManager::instance()->on( - 'Controller.initialize', - [$this->_test, 'controllerSpy'] - ); - - $server = new Server($this->app); - $psrRequest = $this->_createRequest($request); - - return $server->run($psrRequest); - } - /** * Create a PSR7 request from the request spec. * * @param array $spec The request spec. - * @return \Psr\Http\Message\RequestInterface + * @return \Psr\Http\Message\ServerRequestInterface */ protected function _createRequest($spec) { @@ -161,14 +146,18 @@ protected function _createRequest($spec) $spec['post'] = []; } $environment = array_merge( - array_merge($_SERVER, ['REQUEST_URI' => $spec['url'], 'PHP_SELF' => '/']), + array_merge($_SERVER, ['REQUEST_URI' => $spec['url']]), $spec['environment'] ); + if (strpos($environment['PHP_SELF'], 'phpunit') !== false) { + $environment['PHP_SELF'] = '/'; + } $request = ServerRequestFactory::fromGlobals( $environment, $spec['query'], $spec['post'], - $spec['cookies'] + $spec['cookies'], + $spec['files'] ); $request = $request->withAttribute('session', $spec['session']); @@ -181,4 +170,34 @@ protected function _createRequest($spec) return $request; } + + /** + * Run a request and get the response. + * + * @param array $requestSpec The request spec to execute. + * @return \Psr\Http\Message\ResponseInterface The generated response. + */ + public function execute($requestSpec) + { + try { + $reflect = new ReflectionClass($this->_class); + $app = $reflect->newInstanceArgs($this->_constructorArgs); + } catch (ReflectionException $e) { + throw new LogicException(sprintf( + 'Cannot load "%s" for use in integration testing.', + $this->_class + )); + } + + // Spy on the controller using the initialize hook instead + // of the dispatcher hooks as those will be going away one day. + EventManager::instance()->on( + 'Controller.initialize', + [$this->_test, 'controllerSpy'] + ); + + $server = new Server($app); + + return $server->run($this->_createRequest($requestSpec)); + } } diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Stub/ConsoleInput.php b/app/vendor/cakephp/cakephp/src/TestSuite/Stub/ConsoleInput.php new file mode 100644 index 000000000..0157f56c9 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Stub/ConsoleInput.php @@ -0,0 +1,87 @@ +replies = $replies; + } + + /** + * Read a reply + * + * @return mixed The value of the reply + */ + public function read() + { + $this->currentIndex += 1; + + if (!isset($this->replies[$this->currentIndex])) { + $total = count($this->replies); + $formatter = new NumberFormatter('en', NumberFormatter::ORDINAL); + $nth = $formatter->format($this->currentIndex + 1); + + $replies = implode(', ', $this->replies); + $message = "There are no more input replies available. This is the {$nth} read operation, " . + "only {$total} replies were set. The provided replies are: {$replies}"; + throw new ConsoleException($message); + } + + return $this->replies[$this->currentIndex]; + } + + /** + * Check if data is available on stdin + * + * @param int $timeout An optional time to wait for data + * @return bool True for data available, false otherwise + */ + public function dataAvailable($timeout = 0) + { + return true; + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/TestCase.php b/app/vendor/cakephp/cakephp/src/TestSuite/TestCase.php index d990a24dd..d8c987d9a 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/TestCase.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/TestCase.php @@ -15,8 +15,10 @@ use Cake\Core\App; use Cake\Core\Configure; +use Cake\Core\Plugin; use Cake\Datasource\ConnectionManager; use Cake\Event\EventManager; +use Cake\Http\BaseApplication; use Cake\ORM\Entity; use Cake\ORM\Exception\MissingTableClassException; use Cake\ORM\Locator\LocatorAwareTrait; @@ -189,6 +191,65 @@ public function loadFixtures() } } + /** + * Load plugins into a simulated application. + * + * Useful to test how plugins being loaded/not loaded interact with other + * elements in CakePHP or applications. + * + * @param array $plugins List of Plugins to load. + * @return \Cake\Http\BaseApplication + */ + public function loadPlugins(array $plugins = []) + { + /** @var \Cake\Http\BaseApplication $app */ + $app = $this->getMockForAbstractClass( + BaseApplication::class, + [''] + ); + + foreach ($plugins as $pluginName => $config) { + if (is_array($config)) { + $app->addPlugin($pluginName, $config); + } else { + $app->addPlugin($config); + } + } + $app->pluginBootstrap(); + $builder = Router::createRouteBuilder('/'); + $app->pluginRoutes($builder); + + return $app; + } + + /** + * Remove plugins from the global plugin collection. + * + * Useful in test case teardown methods. + * + * @param string[] $names A list of plugins you want to remove. + * @return void + */ + public function removePlugins(array $names = []) + { + $collection = Plugin::getCollection(); + foreach ($names as $name) { + $collection->remove($name); + } + } + + /** + * Clear all plugins from the global plugin collection. + * + * Useful in test case teardown methods. + * + * @return void + */ + public function clearPlugins() + { + Plugin::getCollection()->clear(); + } + /** * Asserts that a global event was fired. You must track events in your event manager for this assertion to work * @@ -431,7 +492,7 @@ public function assertHtml($expected, $string, $fullDebug = false) $tags = (string)$tags; } $i++; - if (is_string($tags) && $tags{0} === '<') { + if (is_string($tags) && $tags[0] === '<') { $tags = [substr($tags, 1) => []]; } elseif (is_string($tags)) { $tagsTrimmed = preg_replace('/\s+/m', '', $tags); @@ -673,12 +734,12 @@ protected function skipUnless($condition, $message = '') * Mock a model, maintain fixtures and table association * * @param string $alias The model to get a mock for. - * @param array $methods The list of methods to mock + * @param string[]|null $methods The list of methods to mock * @param array $options The config data for the mock's constructor. * @throws \Cake\ORM\Exception\MissingTableClassException * @return \Cake\ORM\Table|\PHPUnit_Framework_MockObject_MockObject */ - public function getMockForModel($alias, array $methods = [], array $options = []) + public function getMockForModel($alias, $methods = [], array $options = []) { /** @var \Cake\ORM\Table $className */ $className = $this->_getTableClassName($alias, $options); diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/TestEmailTransport.php b/app/vendor/cakephp/cakephp/src/TestSuite/TestEmailTransport.php new file mode 100644 index 000000000..da2c6d842 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/TestEmailTransport.php @@ -0,0 +1,81 @@ +_validCiphers)) { $msg = sprintf( - 'Invalid encryption cipher. Must be one of %s.', + 'Invalid encryption cipher. Must be one of %s or false.', implode(', ', $this->_validCiphers) ); throw new RuntimeException($msg); @@ -128,7 +128,18 @@ protected function _decode($value, $encrypt, $key) } $this->_checkCipher($encrypt); $prefix = 'Q2FrZQ==.'; - $value = base64_decode(substr($value, strlen($prefix))); + $prefixLength = strlen($prefix); + + if (strncmp($value, $prefix, $prefixLength) !== 0) { + return ''; + } + + $value = base64_decode(substr($value, $prefixLength), true); + + if ($value === false || $value === '') { + return ''; + } + if ($key === null) { $key = $this->_getCookieEncryptionKey(); } @@ -139,6 +150,10 @@ protected function _decode($value, $encrypt, $key) $value = Security::decrypt($value, $key); } + if ($value === false) { + return ''; + } + return $this->_explode($value); } diff --git a/app/vendor/cakephp/cakephp/src/Utility/Hash.php b/app/vendor/cakephp/cakephp/src/Utility/Hash.php index 31575a8de..24f5a56e8 100644 --- a/app/vendor/cakephp/cakephp/src/Utility/Hash.php +++ b/app/vendor/cakephp/cakephp/src/Utility/Hash.php @@ -253,8 +253,13 @@ protected static function _matches($data, $selector) return false; } + if (is_array($data)) { + $attrPresent = array_key_exists($attr, $data); + } else { + $attrPresent = $data->offsetExists($attr); + } // Empty attribute = fail. - if (!(isset($data[$attr]) || array_key_exists($attr, $data))) { + if (!$attrPresent) { return false; } @@ -997,9 +1002,9 @@ public static function sort(array $data, $path, $dir = 'asc', $type = 'regular') $type = strtolower($type); if ($dir === 'asc') { - $dir = SORT_ASC; + $dir = \SORT_ASC; } else { - $dir = SORT_DESC; + $dir = \SORT_DESC; } if ($type === 'numeric') { $type = \SORT_NUMERIC; diff --git a/app/vendor/cakephp/cakephp/src/Utility/Inflector.php b/app/vendor/cakephp/cakephp/src/Utility/Inflector.php index c970f48e6..8fcec95b1 100644 --- a/app/vendor/cakephp/cakephp/src/Utility/Inflector.php +++ b/app/vendor/cakephp/cakephp/src/Utility/Inflector.php @@ -556,7 +556,7 @@ public static function singularize($word) if (preg_match('/(.*?(?:\\b|_))(' . static::$_cache['irregular']['singular'] . ')$/i', $word, $regs)) { static::$_cache['singularize'][$word] = $regs[1] . substr($regs[2], 0, 1) . - substr(array_search(strtolower($regs[2]), static::$_irregular), 1); + substr(array_search(strtolower($regs[2]), static::$_irregular, true), 1); return static::$_cache['singularize'][$word]; } diff --git a/app/vendor/cakephp/cakephp/src/Utility/MergeVariablesTrait.php b/app/vendor/cakephp/cakephp/src/Utility/MergeVariablesTrait.php index 6f676518d..8517cc7a0 100644 --- a/app/vendor/cakephp/cakephp/src/Utility/MergeVariablesTrait.php +++ b/app/vendor/cakephp/cakephp/src/Utility/MergeVariablesTrait.php @@ -28,7 +28,7 @@ trait MergeVariablesTrait * - `associative` - A list of properties that should be treated as associative arrays. * Properties in this list will be passed through Hash::normalize() before merging. * - * @param array $properties An array of properties and the merge strategy for them. + * @param string[] $properties An array of properties and the merge strategy for them. * @param array $options The options to use when merging properties. * @return void */ diff --git a/app/vendor/cakephp/cakephp/src/Utility/Text.php b/app/vendor/cakephp/cakephp/src/Utility/Text.php index b5760407b..06d34034c 100644 --- a/app/vendor/cakephp/cakephp/src/Utility/Text.php +++ b/app/vendor/cakephp/cakephp/src/Utility/Text.php @@ -22,10 +22,17 @@ class Text { + /** + * Default transliterator. + * + * @var \Transliterator Transliterator instance. + */ + protected static $_defaultTransliterator; + /** * Default transliterator id string. * - * @param string $_defaultTransliteratorId Transliterator identifier string. + * @var string $_defaultTransliteratorId Transliterator identifier string. */ protected static $_defaultTransliteratorId = 'Any-Latin; Latin-ASCII; [\u0080-\u7fff] remove'; @@ -84,7 +91,7 @@ public static function uuid() * @param string $separator The token to split the data on. * @param string $leftBound The left boundary to ignore separators in. * @param string $rightBound The right boundary to ignore separators in. - * @return array|string Array of tokens in $data or original input if empty. + * @return string|string[] Array of tokens in $data or original input if empty. */ public static function tokenize($data, $separator = ',', $leftBound = '(', $rightBound = ')') { @@ -492,7 +499,13 @@ public static function highlight($text, $phrase, array $options = []) 'limit' => -1, ]; $options += $defaults; - $html = $format = $ellipsis = $exact = $limit = null; + + $html = $format = $limit = null; + /** + * @var bool $html + * @var string|array $format + * @var int $limit + */ extract($options); if (is_array($phrase)) { @@ -548,7 +561,7 @@ public static function stripLinks($text) * * ### Options: * - * - `ellipsis` Will be used as Beginning and prepended to the trimmed string + * - `ellipsis` Will be used as beginning and prepended to the trimmed string * - `exact` If false, $text will not be cut mid-word * * @param string $text String to truncate. @@ -563,6 +576,10 @@ public static function tail($text, $length = 100, array $options = []) ]; $options += $default; $exact = $ellipsis = null; + /** + * @var string $ellipsis + * @var bool $exact + */ extract($options); if (mb_strlen($text) <= $length) { @@ -630,7 +647,7 @@ public static function truncate($text, $length = 100, array $options = []) if (preg_match('/<[\w]+[^>]*>/', $tag[0])) { array_unshift($openTags, $tag[2]); } elseif (preg_match('/<\/([\w]+)[^>]*>/', $tag[0], $closeTag)) { - $pos = array_search($closeTag[1], $openTags); + $pos = array_search($closeTag[1], $openTags, true); if ($pos !== false) { array_splice($openTags, $pos, 1); } @@ -905,7 +922,7 @@ public static function excerpt($text, $phrase, $radius = 100, $ellipsis = '...') /** * Creates a comma separated list where the last two items are joined with 'and', forming natural language. * - * @param array $list The list to be joined. + * @param string[] $list The list to be joined. * @param string|null $and The word used to join the last and second last items together with. Defaults to 'and'. * @param string $separator The separator used to join all the other items together. Defaults to ', '. * @return string The glued together string. @@ -1028,10 +1045,10 @@ public static function parseFileSize($size, $default = false) $size = strtoupper($size); $l = -2; - $i = array_search(substr($size, -2), ['KB', 'MB', 'GB', 'TB', 'PB']); + $i = array_search(substr($size, -2), ['KB', 'MB', 'GB', 'TB', 'PB'], true); if ($i === false) { $l = -1; - $i = array_search(substr($size, -1), ['K', 'M', 'G', 'T', 'P']); + $i = array_search(substr($size, -1), ['K', 'M', 'G', 'T', 'P'], true); } if ($i !== false) { $size = (float)substr($size, 0, $l); @@ -1051,6 +1068,30 @@ public static function parseFileSize($size, $default = false) throw new InvalidArgumentException('No unit type.'); } + /** + * Get the default transliterator. + * + * @return \Transliterator|null Either a Transliterator instance, or `null` + * in case no transliterator has been set yet. + * @since 3.7.0 + */ + public static function getTransliterator() + { + return static::$_defaultTransliterator; + } + + /** + * Set the default transliterator. + * + * @param \Transliterator $transliterator A `Transliterator` instance. + * @return void + * @since 3.7.0 + */ + public static function setTransliterator(\Transliterator $transliterator) + { + static::$_defaultTransliterator = $transliterator; + } + /** * Get default transliterator identifier string. * @@ -1069,6 +1110,7 @@ public static function getTransliteratorId() */ public static function setTransliteratorId($transliteratorId) { + static::setTransliterator(transliterator_create($transliteratorId)); static::$_defaultTransliteratorId = $transliteratorId; } @@ -1076,16 +1118,20 @@ public static function setTransliteratorId($transliteratorId) * Transliterate string. * * @param string $string String to transliterate. - * @param string|null $transliteratorId Transliterator identifier. If null - * Text::$_defaultTransliteratorId will be used. + * @param \Transliterator|string|null $transliterator Either a Transliterator + * instance, or a transliterator identifier string. If `null`, the default + * transliterator (identifier) set via `setTransliteratorId()` or + * `setTransliterator()` will be used. * @return string * @see https://secure.php.net/manual/en/transliterator.transliterate.php */ - public static function transliterate($string, $transliteratorId = null) + public static function transliterate($string, $transliterator = null) { - $transliteratorId = $transliteratorId ?: static::$_defaultTransliteratorId; + if (!$transliterator) { + $transliterator = static::$_defaultTransliterator ?: static::$_defaultTransliteratorId; + } - return transliterator_transliterate($transliteratorId, $string); + return transliterator_transliterate($transliterator, $string); } /** @@ -1095,8 +1141,9 @@ public static function transliterate($string, $transliteratorId = null) * ### Options: * * - `replacement`: Replacement string. Default '-'. - * - `transliteratorId`: A valid tranliterator id string. - * If default `null` Text::$_defaultTransliteratorId to be used. + * - `transliteratorId`: A valid transliterator id string. + * If `null` (default) the transliterator (identifier) set via + * `setTransliteratorId()` or `setTransliterator()` will be used. * If `false` no transliteration will be done, only non words will be removed. * - `preserve`: Specific non-word character to preserve. Default `null`. * For e.g. this option can be set to '.' to generate clean file names. @@ -1105,6 +1152,8 @@ public static function transliterate($string, $transliteratorId = null) * @param array $options If string it will be use as replacement character * or an array of options. * @return string + * @see setTransliterator() + * @see setTransliteratorId() */ public static function slug($string, $options = []) { diff --git a/app/vendor/cakephp/cakephp/src/Utility/Xml.php b/app/vendor/cakephp/cakephp/src/Utility/Xml.php index 01346d608..2ab5c37c1 100644 --- a/app/vendor/cakephp/cakephp/src/Utility/Xml.php +++ b/app/vendor/cakephp/cakephp/src/Utility/Xml.php @@ -169,6 +169,51 @@ protected static function _loadXml($input, $options) } } + /** + * Parse the input html string and create either a SimpleXmlElement object or a DOMDocument. + * + * @param string $input The input html string to load. + * @param array $options The options to use. See Xml::build() + * @return \SimpleXMLElement|\DOMDocument + * @throws \Cake\Utility\Exception\XmlException + */ + public static function loadHtml($input, $options = []) + { + $defaults = [ + 'return' => 'simplexml', + 'loadEntities' => false, + ]; + $options += $defaults; + + $hasDisable = function_exists('libxml_disable_entity_loader'); + $internalErrors = libxml_use_internal_errors(true); + if ($hasDisable && !$options['loadEntities']) { + libxml_disable_entity_loader(true); + } + $flags = 0; + if (!empty($options['parseHuge'])) { + $flags |= LIBXML_PARSEHUGE; + } + try { + $xml = new DOMDocument(); + $xml->loadHTML($input, $flags); + + if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') { + $flags |= LIBXML_NOCDATA; + $xml = simplexml_import_dom($xml); + } + + return $xml; + } catch (Exception $e) { + throw new XmlException('Xml cannot be read. ' . $e->getMessage(), null, $e); + } finally { + if ($hasDisable && !$options['loadEntities']) { + libxml_disable_entity_loader(false); + } + libxml_use_internal_errors($internalErrors); + } + } + /** * Transform an array into a SimpleXMLElement * @@ -398,7 +443,7 @@ public static function toArray($obj) * @param \SimpleXMLElement $xml SimpleXMLElement object * @param array $parentData Parent array with data * @param string $ns Namespace of current child - * @param array $namespaces List of namespaces in XML + * @param string[] $namespaces List of namespaces in XML * @return void */ protected static function _toArray($xml, &$parentData, $ns, $namespaces) diff --git a/app/vendor/cakephp/cakephp/src/Validation/Validation.php b/app/vendor/cakephp/cakephp/src/Validation/Validation.php index 4366ecba5..82312df31 100644 --- a/app/vendor/cakephp/cakephp/src/Validation/Validation.php +++ b/app/vendor/cakephp/cakephp/src/Validation/Validation.php @@ -76,6 +76,11 @@ class Validation */ const COMPARE_LESS_OR_EQUAL = '<='; + /** + * Datetime ISO8601 format + */ + const DATETIME_ISO8601 = 'iso8601'; + /** * Some complex patterns needed in multiple places * @@ -184,6 +189,29 @@ public static function blank($check) return !static::_check($check, '/[^\\s]/'); } + /** + * Backwards compatibility wrapper for Validation::creditCard(). + * + * @param string $check credit card number to validate + * @param string|string[] $type 'all' may be passed as a string, defaults to fast which checks format of most major credit cards + * if an array is used only the values of the array are checked. + * Example: ['amex', 'bankcard', 'maestro'] + * @param bool $deep set to true this will check the Luhn algorithm of the credit card. + * @param string|null $regex A custom regex can also be passed, this will be used instead of the defined regex values + * @return bool Success + * @deprecated 3.7.0 Use Validation::creditCard() instead. + * @see \Cake\Validation\Validation::creditCard() + */ + public static function cc($check, $type = 'fast', $deep = false, $regex = null) + { + deprecationWarning( + 'Validation::cc() is deprecated. ' . + 'Use Validation::creditCard() instead.' + ); + + return static::creditCard($check, $type, $deep, $regex); + } + /** * Validation of credit card numbers. * Returns true if $check is in the proper credit card format. @@ -197,7 +225,7 @@ public static function blank($check) * @return bool Success * @see \Cake\Validation\Validation::luhn() */ - public static function cc($check, $type = 'fast', $deep = false, $regex = null) + public static function creditCard($check, $type = 'fast', $deep = false, $regex = null) { if (!is_scalar($check)) { return false; @@ -540,6 +568,7 @@ public static function date($check, $format = 'ymd', $regex = null) * * @param string|\DateTimeInterface $check Value to check * @param string|array $dateFormat Format of the date part. See Validation::date() for more information. + * Or `Validation::DATETIME_ISO8601` to valid an ISO8601 datetime value * @param string|null $regex Regex for the date part. If a custom regular expression is used this is the only validation that will occur. * @return bool True if the value is valid, false otherwise * @see \Cake\Validation\Validation::date() @@ -553,21 +582,54 @@ public static function datetime($check, $dateFormat = 'ymd', $regex = null) if (is_object($check)) { return false; } + if ($dateFormat === static::DATETIME_ISO8601 && !static::iso8601($check)) { + return false; + } + $valid = false; if (is_array($check)) { $check = static::_getDateString($check); $dateFormat = 'ymd'; } - $parts = explode(' ', $check); + $parts = preg_split("/[\sT]+/", $check); if (!empty($parts) && count($parts) > 1) { $date = rtrim(array_shift($parts), ','); $time = implode(' ', $parts); + if ($dateFormat === static::DATETIME_ISO8601) { + $dateFormat = 'ymd'; + $time = preg_split("/[TZ\-\+\.]/", $time); + $time = array_shift($time); + } $valid = static::date($date, $dateFormat, $regex) && static::time($time); } return $valid; } + /** + * Validates an iso8601 datetime format + * ISO8601 recognize datetime like 2019 as a valid date. To validate and check date integrity, use @see \Cake\Validation\Validation::datetime() + * + * @param string|\DateTimeInterface $check Value to check + * + * @return bool True if the value is valid, false otherwise + * + * @see Regex credits: https://www.myintervals.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/ + */ + public static function iso8601($check) + { + if ($check instanceof DateTimeInterface) { + return true; + } + if (is_object($check)) { + return false; + } + + $regex = '/^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/'; + + return static::_check($check, $regex); + } + /** * Time validation, determines if the string passed is a valid time. * Validates time as 24hr (HH:MM) or am/pm ([H]H:MM[a|p]m) @@ -829,6 +891,10 @@ public static function ip($check, $type = 'both') */ public static function minLength($check, $min) { + if (!is_scalar($check)) { + return false; + } + return mb_strlen($check) >= $min; } @@ -841,6 +907,10 @@ public static function minLength($check, $min) */ public static function maxLength($check, $max) { + if (!is_scalar($check)) { + return false; + } + return mb_strlen($check) <= $max; } @@ -853,6 +923,10 @@ public static function maxLength($check, $max) */ public static function minLengthBytes($check, $min) { + if (!is_scalar($check)) { + return false; + } + return strlen($check) >= $min; } @@ -865,6 +939,10 @@ public static function minLengthBytes($check, $min) */ public static function maxLengthBytes($check, $max) { + if (!is_scalar($check)) { + return false; + } + return strlen($check) <= $max; } @@ -1030,7 +1108,7 @@ public static function url($check, $strict = false) * Checks if a value is in a given list. Comparison is case sensitive by default. * * @param string $check Value to check. - * @param array $list List to check against. + * @param string[] $list List to check against. * @param bool $caseInsensitive Set to true for case insensitive comparison. * @return bool Success. */ @@ -1163,7 +1241,7 @@ public static function mimeType($check, $mimeTypes = []) $mimeTypes[$key] = strtolower($val); } - return in_array($mime, $mimeTypes); + return in_array(strtolower($mime), $mimeTypes); } /** @@ -1313,9 +1391,10 @@ public static function uploadedFile($file, array $options = []) /** * Validates the size of an uploaded image. * - * @param array|\Psr\Http\Message\UploadedFileInterface $file The uploaded file data from PHP. + * @param array|\Psr\Http\Message\UploadedFileInterface $file The uploaded file data from PHP. * @param array $options Options to validate width and height. * @return bool + * @throws \InvalidArgumentException */ public static function imageSize($file, $options) { @@ -1323,9 +1402,11 @@ public static function imageSize($file, $options) throw new InvalidArgumentException('Invalid image size validation parameters! Missing `width` and / or `height`.'); } - $file = static::getFilename($file); + $filename = static::getFilename($file); - list($width, $height) = getimagesize($file); + list($width, $height) = getimagesize($filename); + + $validHeight = $validWidth = null; if (isset($options['height'])) { $validHeight = self::comparison($height, $options['height'][0], $options['height'][1]); @@ -1333,13 +1414,13 @@ public static function imageSize($file, $options) if (isset($options['width'])) { $validWidth = self::comparison($width, $options['width'][0], $options['width'][1]); } - if (isset($validHeight, $validWidth)) { + if ($validHeight !== null && $validWidth !== null) { return ($validHeight && $validWidth); } - if (isset($validHeight)) { + if ($validHeight !== null) { return $validHeight; } - if (isset($validWidth)) { + if ($validWidth !== null) { return $validWidth; } @@ -1510,7 +1591,7 @@ public static function utf8($value, array $options = []) */ public static function isInteger($value) { - if (!is_scalar($value) || is_float($value)) { + if (!is_numeric($value) || is_float($value)) { return false; } if (is_int($value)) { @@ -1556,6 +1637,41 @@ public static function hexColor($check) return static::_check($check, '/^#[0-9a-f]{6}$/iD'); } + /** + * Check that the input value has a valid International Bank Account Number IBAN syntax + * Requirements are uppercase, no whitespaces, max length 34, country code and checksum exist at right spots, + * body matches against checksum via Mod97-10 algorithm + * + * @param string $check The value to check + * + * @return bool Success + */ + public static function iban($check) + { + if (!preg_match('/^[A-Z]{2}[0-9]{2}[A-Z0-9]{1,30}$/', $check)) { + return false; + } + + $country = substr($check, 0, 2); + $checkInt = intval(substr($check, 2, 2)); + $account = substr($check, 4); + $search = range('A', 'Z'); + $replace = []; + foreach (range(10, 35) as $tmp) { + $replace[] = strval($tmp); + } + $numStr = str_replace($search, $replace, $account . $country . '00'); + $checksum = intval(substr($numStr, 0, 1)); + $numStrLength = strlen($numStr); + for ($pos = 1; $pos < $numStrLength; $pos++) { + $checksum *= 10; + $checksum += intval(substr($numStr, $pos, 1)); + $checksum %= 97; + } + + return ((98 - $checksum) === $checkInt); + } + /** * Converts an array representing a date or datetime into a ISO string. * The arrays are typically sent for validation from a form generated by diff --git a/app/vendor/cakephp/cakephp/src/Validation/Validator.php b/app/vendor/cakephp/cakephp/src/Validation/Validator.php index 7d6c23cd7..49949719c 100644 --- a/app/vendor/cakephp/cakephp/src/Validation/Validator.php +++ b/app/vendor/cakephp/cakephp/src/Validation/Validator.php @@ -37,6 +37,62 @@ class Validator implements ArrayAccess, IteratorAggregate, Countable */ const NESTED = '_nested'; + /** + * A flag for allowEmptyFor() + * + * When an empty string is given, it will be recognized as empty. + * + * @var int + */ + const EMPTY_STRING = 1; + + /** + * A flag for allowEmptyFor() + * + * When an empty array is given, it will be recognized as empty. + * + * @var int + */ + const EMPTY_ARRAY = 2; + + /** + * A flag for allowEmptyFor() + * + * When an array is given, if it has at least the `name`, `type`, `tmp_name` and `error` keys, + * and the value of `error` is equal to `UPLOAD_ERR_NO_FILE`, the value will be recognized as + * empty. + * + * @var int + */ + const EMPTY_FILE = 4; + + /** + * A flag for allowEmptyFor() + * + * When an array is given, if it contains the `year` key, and only empty strings + * or null values, it will be recognized as empty. + * + * @var int + */ + const EMPTY_DATE = 8; + + /** + * A flag for allowEmptyFor() + * + * When an array is given, if it contains the `hour` key, and only empty strings + * or null values, it will be recognized as empty. + * + * @var int + */ + const EMPTY_TIME = 16; + + /** + * A combination of the all EMPTY_* flags + * + * @var int + */ + const EMPTY_ALL = self::EMPTY_STRING | self::EMPTY_ARRAY | self::EMPTY_FILE | self::EMPTY_DATE | self::EMPTY_TIME; + /** * Holds the ValidationSet objects array * @@ -82,6 +138,13 @@ class Validator implements ArrayAccess, IteratorAggregate, Countable */ protected $_allowEmptyMessages = []; + /** + * Contains the flags which specify what is empty for each corresponding field. + * + * @var array + */ + protected $_allowEmptyFlags = []; + /** * Constructor * @@ -104,14 +167,6 @@ public function errors(array $data, $newRecord = true) { $errors = []; - $requiredMessage = 'This field is required'; - $emptyMessage = 'This field cannot be left empty'; - - if ($this->_useI18n) { - $requiredMessage = __d('cake', 'This field is required'); - $emptyMessage = __d('cake', 'This field cannot be left empty'); - } - foreach ($this->_fields as $name => $field) { $keyPresent = array_key_exists($name, $data); @@ -119,9 +174,7 @@ public function errors(array $data, $newRecord = true) $context = compact('data', 'newRecord', 'field', 'providers'); if (!$keyPresent && !$this->_checkPresence($field, $context)) { - $errors[$name]['_required'] = isset($this->_presenceMessages[$name]) - ? $this->_presenceMessages[$name] - : $requiredMessage; + $errors[$name]['_required'] = $this->getRequiredMessage($name); continue; } if (!$keyPresent) { @@ -129,12 +182,16 @@ public function errors(array $data, $newRecord = true) } $canBeEmpty = $this->_canBeEmpty($field, $context); - $isEmpty = $this->_fieldIsEmpty($data[$name]); + + $flags = static::EMPTY_ALL; + if (isset($this->_allowEmptyFlags[$name])) { + $flags = $this->_allowEmptyFlags[$name]; + } + + $isEmpty = $this->isEmpty($data[$name], $flags); if (!$canBeEmpty && $isEmpty) { - $errors[$name]['_empty'] = isset($this->_allowEmptyMessages[$name]) - ? $this->_allowEmptyMessages[$name] - : $emptyMessage; + $errors[$name]['_empty'] = $this->getNotEmptyMessage($name); continue; } @@ -249,7 +306,7 @@ public static function addDefaultProvider($name, $object) /** * Get the list of default providers. * - * @return array + * @return string[] */ public static function getDefaultProviders() { @@ -286,7 +343,7 @@ public function provider($name, $object = null) /** * Get the list of providers in this validator. * - * @return array + * @return string[] */ public function providers() { @@ -389,7 +446,7 @@ public function count() */ public function add($field, $name, $rule = []) { - $field = $this->field($field); + $validationSet = $this->field($field); if (!is_array($name)) { $rules = [$name => $rule]; @@ -401,7 +458,7 @@ public function add($field, $name, $rule = []) if (is_array($rule)) { $rule += ['rule' => $name]; } - $field->add($name, $rule); + $validationSet->add($name, $rule); } return $this; @@ -431,8 +488,8 @@ public function addNested($field, Validator $validator, $message = null, $when = { $extra = array_filter(['message' => $message, 'on' => $when]); - $field = $this->field($field); - $field->add(static::NESTED, $extra + ['rule' => function ($value, $context) use ($validator, $message) { + $validationSet = $this->field($field); + $validationSet->add(static::NESTED, $extra + ['rule' => function ($value, $context) use ($validator, $message) { if (!is_array($value)) { return false; } @@ -473,8 +530,8 @@ public function addNestedMany($field, Validator $validator, $message = null, $wh { $extra = array_filter(['message' => $message, 'on' => $when]); - $field = $this->field($field); - $field->add(static::NESTED, $extra + ['rule' => function ($value, $context) use ($validator, $message) { + $validationSet = $this->field($field); + $validationSet->add(static::NESTED, $extra + ['rule' => function ($value, $context) use ($validator, $message) { if (!is_array($value)) { return false; } @@ -627,6 +684,8 @@ public function requirePresence($field, $mode = true, $message = null) * Because this and `notEmpty()` modify the same internal state, the last * method called will take precedence. * + * @deprecated 3.7.0 Use allowEmptyString(), allowEmptyArray(), allowEmptyFile(), + * allowEmptyDate(), allowEmptyTime() or allowEmptyDateTime() instead. * @param string|array $field the name of the field or a list of fields * @param bool|string|callable $when Indicates when the field is allowed to be empty * Valid values are true (always), 'create', 'update'. If a callable is passed then @@ -636,28 +695,415 @@ public function requirePresence($field, $mode = true, $message = null) */ public function allowEmpty($field, $when = true, $message = null) { - $settingsDefault = [ + $defaults = [ 'when' => $when, - 'message' => $message + 'message' => $message, ]; - if (!is_array($field)) { - $field = $this->_convertValidatorToArray($field, $settingsDefault); + $field = $this->_convertValidatorToArray($field, $defaults); } foreach ($field as $fieldName => $setting) { - $settings = $this->_convertValidatorToArray($fieldName, $settingsDefault, $setting); - $fieldName = current(array_keys($settings)); + $settings = $this->_convertValidatorToArray($fieldName, $defaults, $setting); + $fieldName = array_keys($settings)[0]; + $this->allowEmptyFor($fieldName, null, $settings[$fieldName]['when'], $settings[$fieldName]['message']); + } - $this->field($fieldName)->allowEmpty($settings[$fieldName]['when']); - if ($settings[$fieldName]['message']) { - $this->_allowEmptyMessages[$fieldName] = $settings[$fieldName]['message']; - } + return $this; + } + + /** + * Low-level method to indicate that a field can be empty. + * + * This method should generally not be used and instead you should + * use: + * + * - `allowEmptyString()` + * - `allowEmptyArray()` + * - `allowEmptyFile()` + * - `allowEmptyDate()` + * - `allowEmptyDatetime()` + * - `allowEmptyTime()` + * + * Should be used as their APIs are simpler to operate and read. + * + * You can also set flags, when and message for all passed fields, the individual + * setting takes precedence over group settings. + * + * ### Example: + * + * ``` + * // Email can be empty + * $validator->allowEmptyFor('email', Validator::EMPTY_STRING); + * + * // Email can be empty on create + * $validator->allowEmptyFor('email', Validator::EMPTY_STRING, 'create'); + * + * // Email can be empty on update + * $validator->allowEmptyFor('email', Validator::EMPTY_STRING, 'update'); + * ``` + * + * It is possible to conditionally allow emptiness on a field by passing a callback + * as a second argument. The callback will receive the validation context array as + * argument: + * + * ``` + * $validator->allowEmpty('email', Validator::EMPTY_STRING, function ($context) { + * return !$context['newRecord'] || $context['data']['role'] === 'admin'; + * }); + * ``` + * + * If you want to allow other kind of empty data on a field, you need to pass other + * flags: + * + * ``` + * $validator->allowEmptyFor('photo', Validator::EMPTY_FILE); + * $validator->allowEmptyFor('published', Validator::EMPTY_STRING | Validator::EMPTY_DATE | Validator::EMPTY_TIME); + * $validator->allowEmptyFor('items', Validator::EMPTY_STRING | Validator::EMPTY_ARRAY); + * ``` + * + * You can also use convenience wrappers of this method. The following calls are the + * same as above: + * + * ``` + * $validator->allowEmptyFile('photo'); + * $validator->allowEmptyDateTime('published'); + * $validator->allowEmptyArray('items'); + * ``` + * + * @param string $field The name of the field. + * @param int|null $flags A bitmask of EMPTY_* flags which specify what is empty + * @param bool|string|callable $when Indicates when the field is allowed to be empty + * Valid values are true, false, 'create', 'update'. If a callable is passed then + * the field will allowed to be empty only when the callback returns true. + * @param string|null $message The message to show if the field is not + * @since 3.7.0 + * @return $this + */ + public function allowEmptyFor($field, $flags, $when = true, $message = null) + { + $this->field($field)->allowEmpty($when); + if ($message) { + $this->_allowEmptyMessages[$field] = $message; + } + if ($flags !== null) { + $this->_allowEmptyFlags[$field] = $flags; } return $this; } + /** + * Compatibility shim for the allowEmpty* methods that enable + * us to support both the `$when, $message` signature (deprecated) + * and the `$message, $when` format which is preferred. + * + * A deprecation warning will be emitted when a deprecated form + * is used. + * + * @param mixed $first The message or when to be sorted. + * @param mixed $second The message or when to be sorted. + * @param string $method The called method + * @return array A list of [$message, $when] + */ + protected function sortMessageAndWhen($first, $second, $method) + { + // Called with `$message, $when`. No order change necessary + if (( + in_array($second, [true, false, 'create', 'update'], true) || + is_callable($second) + ) && ( + is_string($first) || $first === null + ) && ( + $first !== 'create' && $first !== 'update' + ) + ) { + return [$first, $second]; + } + deprecationWarning( + "You are using a deprecated argument order for ${method}. " . + "You should reverse the order of your `when` and `message` arguments " . + "so that they are `message, when`." + ); + + // Called without the second argument. + if (is_bool($second)) { + $second = null; + } + + // Called with `$when, $message`. Reverse the + // order to match the expected return value. + return [$second, $first]; + } + + /** + * Allows a field to be an empty string. + * + * This method is equivalent to calling allowEmptyFor() with EMPTY_STRING flag. + * + * @param string $field The name of the field. + * @param string|null $message The message to show if the field is not + * @param bool|string|callable $when Indicates when the field is allowed to be empty + * Valid values are true, false, 'create', 'update'. If a callable is passed then + * the field will allowed to be empty only when the callback returns true. + * @return $this + * @since 3.7.0 + * @see \Cake\Validation\Validator::allowEmptyFor() For detail usage + */ + public function allowEmptyString($field, $message = null, $when = true) + { + list($message, $when) = $this->sortMessageAndWhen($message, $when, __METHOD__); + + return $this->allowEmptyFor($field, self::EMPTY_STRING, $when, $message); + } + + /** + * Requires a field to be not be an empty string. + * + * Opposite to allowEmptyString() + * + * @param string $field The name of the field. + * @param string|null $message The message to show if the field is empty. + * @param bool|string|callable $when Indicates when the field is not allowed + * to be empty. Valid values are false (never), 'create', 'update'. If a + * callable is passed then the field will be required to be not empty when + * the callback returns true. + * @return $this + * @see \Cake\Validation\Validator::allowEmptyString() + * @since 3.8.0 + */ + public function notEmptyString($field, $message = null, $when = false) + { + $when = $this->invertWhenClause($when); + + return $this->allowEmptyFor($field, self::EMPTY_STRING, $when, $message); + } + + /** + * Allows a field to be an empty array. + * + * This method is equivalent to calling allowEmptyFor() with EMPTY_STRING + + * EMPTY_ARRAY flags. + * + * @param string $field The name of the field. + * @param string|null $message The message to show if the field is not + * @param bool|string|callable $when Indicates when the field is allowed to be empty + * Valid values are true, false, 'create', 'update'. If a callable is passed then + * the field will allowed to be empty only when the callback returns true. + * @return $this + * @since 3.7.0 + * @see \Cake\Validation\Validator::allowEmptyFor() for examples. + */ + public function allowEmptyArray($field, $message = null, $when = true) + { + list($message, $when) = $this->sortMessageAndWhen($message, $when, __METHOD__); + + return $this->allowEmptyFor($field, self::EMPTY_STRING | self::EMPTY_ARRAY, $when, $message); + } + + /** + * Require a field to be a non-empty array + * + * Opposite to allowEmptyArray() + * + * @param string $field The name of the field. + * @param string|null $message The message to show if the field is empty. + * @param bool|string|callable $when Indicates when the field is not allowed + * to be empty. Valid values are false (never), 'create', 'update'. If a + * callable is passed then the field will be required to be not empty when + * the callback returns true. + * @return $this + * @see \Cake\Validation\Validator::allowEmptyArray() + * @since 3.8.0 + */ + public function notEmptyArray($field, $message = null, $when = false) + { + $when = $this->invertWhenClause($when); + + return $this->allowEmptyFor($field, self::EMPTY_STRING | self::EMPTY_ARRAY, $when, $message); + } + + /** + * Allows a field to be an empty file. + * + * This method is equivalent to calling allowEmptyFor() with EMPTY_FILE flag. + * File fields will not accept `''`, or `[]` as empty values. Only `null` and a file + * upload with `error` equal to `UPLOAD_ERR_NO_FILE` will be treated as empty. + * + * @param string $field The name of the field. + * @param string|null $message The message to show if the field is not + * @param bool|string|callable $when Indicates when the field is allowed to be empty + * Valid values are true, 'create', 'update'. If a callable is passed then + * the field will allowed to be empty only when the callback returns true. + * @return $this + * @since 3.7.0 + * @see \Cake\Validation\Validator::allowEmptyFor() For detail usage + */ + public function allowEmptyFile($field, $message = null, $when = true) + { + list($message, $when) = $this->sortMessageAndWhen($message, $when, __METHOD__); + + return $this->allowEmptyFor($field, self::EMPTY_FILE, $when, $message); + } + + /** + * Require a field to be a not-empty file. + * + * Opposite to allowEmptyFile() + * + * @param string $field The name of the field. + * @param string|null $message The message to show if the field is empty. + * @param bool|string|callable $when Indicates when the field is not allowed + * to be empty. Valid values are false (never), 'create', 'update'. If a + * callable is passed then the field will be required to be not empty when + * the callback returns true. + * @return $this + * @since 3.8.0 + * @see \Cake\Validation\Validator::allowEmptyFile() + */ + public function notEmptyFile($field, $message = null, $when = false) + { + $when = $this->invertWhenClause($when); + + return $this->allowEmptyFor($field, self::EMPTY_FILE, $when, $message); + } + + /** + * Allows a field to be an empty date. + * + * Empty date values are `null`, `''`, `[]` and arrays where all values are `''` + * and the `year` key is present. + * + * @param string $field The name of the field. + * @param string|null $message The message to show if the field is not + * @param bool|string|callable $when Indicates when the field is allowed to be empty + * Valid values are true, false, 'create', 'update'. If a callable is passed then + * the field will allowed to be empty only when the callback returns true. + * @return $this + * @since 3.7.0 + * @see \Cake\Validation\Validator::allowEmptyFor() for examples + */ + public function allowEmptyDate($field, $message = null, $when = true) + { + list($message, $when) = $this->sortMessageAndWhen($message, $when, __METHOD__); + + return $this->allowEmptyFor($field, self::EMPTY_STRING | self::EMPTY_DATE, $when, $message); + } + + /** + * Require a non-empty date value + * + * @param string $field The name of the field. + * @param string|null $message The message to show if the field is empty. + * @param bool|string|callable $when Indicates when the field is not allowed + * to be empty. Valid values are false (never), 'create', 'update'. If a + * callable is passed then the field will be required to be not empty when + * the callback returns true. + * @return $this + * @since 3.8.0 + * @see \Cake\Validation\Validator::allowEmptyDate() for examples + */ + public function notEmptyDate($field, $message = null, $when = false) + { + $when = $this->invertWhenClause($when); + + return $this->allowEmptyFor($field, self::EMPTY_STRING | self::EMPTY_DATE, $when, $message); + } + + /** + * Allows a field to be an empty time. + * + * Empty date values are `null`, `''`, `[]` and arrays where all values are `''` + * and the `hour` key is present. + * + * This method is equivalent to calling allowEmptyFor() with EMPTY_STRING + + * EMPTY_TIME flags. + * + * @param string $field The name of the field. + * @param string|null $message The message to show if the field is not + * @param bool|string|callable $when Indicates when the field is allowed to be empty + * Valid values are true, false, 'create', 'update'. If a callable is passed then + * the field will allowed to be empty only when the callback returns true. + * @return $this + * @since 3.7.0 + * @see \Cake\Validation\Validator::allowEmptyFor() for examples. + */ + public function allowEmptyTime($field, $message = null, $when = true) + { + list($message, $when) = $this->sortMessageAndWhen($message, $when, __METHOD__); + + return $this->allowEmptyFor($field, self::EMPTY_STRING | self::EMPTY_TIME, $when, $message); + } + + /** + * Require a field to be a non-empty time. + * + * Opposite to allowEmptyTime() + * + * @param string $field The name of the field. + * @param string|null $message The message to show if the field is empty. + * @param bool|string|callable $when Indicates when the field is not allowed + * to be empty. Valid values are false (never), 'create', 'update'. If a + * callable is passed then the field will be required to be not empty when + * the callback returns true. + * @return $this + * @since 3.8.0 + * @see \Cake\Validation\Validator::allowEmptyTime() + */ + public function notEmptyTime($field, $message = null, $when = false) + { + $when = $this->invertWhenClause($when); + + return $this->allowEmptyFor($field, self::EMPTY_STRING | self::EMPTY_TIME, $when, $message); + } + + /** + * Allows a field to be an empty date/time. + * + * Empty date values are `null`, `''`, `[]` and arrays where all values are `''` + * and the `year` and `hour` keys are present. + * + * This method is equivalent to calling allowEmptyFor() with EMPTY_STRING + + * EMPTY_DATE + EMPTY_TIME flags. + * + * @param string $field The name of the field. + * @param string|null $message The message to show if the field is not + * @param bool|string|callable $when Indicates when the field is allowed to be empty + * Valid values are true, false, 'create', 'update'. If a callable is passed then + * the field will allowed to be empty only when the callback returns false. + * @return $this + * @since 3.7.0 + * @see \Cake\Validation\Validator::allowEmptyFor() for examples. + */ + public function allowEmptyDateTime($field, $message = null, $when = true) + { + list($message, $when) = $this->sortMessageAndWhen($message, $when, __METHOD__); + + return $this->allowEmptyFor($field, self::EMPTY_STRING | self::EMPTY_DATE | self::EMPTY_TIME, $when, $message); + } + + /** + * Require a field to be a non empty date/time. + * + * Opposite to allowEmptyDateTime + * + * @param string $field The name of the field. + * @param string|null $message The message to show if the field is empty. + * @param bool|string|callable $when Indicates when the field is not allowed + * to be empty. Valid values are false (never), 'create', 'update'. If a + * callable is passed then the field will be required to be not empty when + * the callback returns true. + * @return $this + * @since 3.8.0 + * @see \Cake\Validation\Validator::allowEmptyDateTime() + */ + public function notEmptyDateTime($field, $message = null, $when = false) + { + $when = $this->invertWhenClause($when); + + return $this->allowEmptyFor($field, self::EMPTY_STRING | self::EMPTY_DATE | self::EMPTY_TIME, $when, $message); + } + /** * Converts validator to fieldName => $settings array * @@ -740,6 +1186,8 @@ protected function _convertValidatorToArray($fieldName, $defaults = [], $setting * Because this and `allowEmpty()` modify the same internal state, the last * method called will take precedence. * + * @deprecated 3.7.0 Use notEmptyString(), notEmptyArray(), notEmptyFile(), + * notEmptyDate(), notEmptyTime() or notEmptyDateTime() instead. * @param string|array $field the name of the field or list of fields * @param string|null $message The message to show if the field is not * @param bool|string|callable $when Indicates when the field is not allowed @@ -762,15 +1210,8 @@ public function notEmpty($field, $message = null, $when = false) foreach ($field as $fieldName => $setting) { $settings = $this->_convertValidatorToArray($fieldName, $defaults, $setting); $fieldName = current(array_keys($settings)); - $whenSetting = $settings[$fieldName]['when']; - - if ($whenSetting === 'create' || $whenSetting === 'update') { - $whenSetting = $whenSetting === 'create' ? 'update' : 'create'; - } elseif (is_callable($whenSetting)) { - $whenSetting = function ($context) use ($whenSetting) { - return !$whenSetting($context); - }; - } + + $whenSetting = $this->invertWhenClause($settings[$fieldName]['when']); $this->field($fieldName)->allowEmpty($whenSetting); if ($settings[$fieldName]['message']) { @@ -781,6 +1222,28 @@ public function notEmpty($field, $message = null, $when = false) return $this; } + /** + * Invert a when clause for creating notEmpty rules + * + * @param bool|string|callable $when Indicates when the field is not allowed + * to be empty. Valid values are true (always), 'create', 'update'. If a + * callable is passed then the field will allowed to be empty only when + * the callback returns false. + * @return bool|string|callable + */ + protected function invertWhenClause($when) + { + if ($when === 'create' || $when === 'update') { + return $when === 'create' ? 'update' : 'create'; + } elseif (is_callable($when)) { + return function ($context) use ($when) { + return !$when($context); + }; + } + + return $when; + } + /** * Add a notBlank rule to a field. * @@ -851,7 +1314,7 @@ public function lengthBetween($field, array $range, $message = null, $when = nul * @param string|null $message The error message when the rule fails. * @param string|callable|null $when Either 'create' or 'update' or a callable that returns * true when the validation rule should be applied. - * @see \Cake\Validation\Validation::cc() + * @see \Cake\Validation\Validation::creditCard() * @return $this */ public function creditCard($field, $type = 'all', $message = null, $when = null) @@ -859,7 +1322,7 @@ public function creditCard($field, $type = 'all', $message = null, $when = null) $extra = array_filter(['on' => $when, 'message' => $message]); return $this->add($field, 'creditCard', $extra + [ - 'rule' => ['cc', $type, true], + 'rule' => ['creditCard', $type, true], ]); } @@ -1956,6 +2419,57 @@ public function regex($field, $regex, $message = null, $when = null) ]); } + /** + * Gets the required message for a field + * + * @param string $field Field name + * @return string|null + */ + public function getRequiredMessage($field) + { + if (!isset($this->_fields[$field])) { + return null; + } + + $defaultMessage = 'This field is required'; + if ($this->_useI18n) { + $defaultMessage = __d('cake', 'This field is required'); + } + + return isset($this->_presenceMessages[$field]) + ? $this->_presenceMessages[$field] + : $defaultMessage; + } + + /** + * Gets the notEmpty message for a field + * + * @param string $field Field name + * @return string|null + */ + public function getNotEmptyMessage($field) + { + if (!isset($this->_fields[$field])) { + return null; + } + + $defaultMessage = 'This field cannot be left empty'; + if ($this->_useI18n) { + $defaultMessage = __d('cake', 'This field cannot be left empty'); + } + + $notBlankMessage = null; + foreach ($this->_fields[$field] as $rule) { + if ($rule->get('rule') === 'notBlank' && $rule->get('message')) { + return $rule->get('message'); + } + } + + return isset($this->_allowEmptyMessages[$field]) + ? $this->_allowEmptyMessages[$field] + : $defaultMessage; + } + /** * Returns false if any validation for the passed rule set should be stopped * due to the field missing in the data array @@ -2012,22 +2526,62 @@ protected function _canBeEmpty($field, $context) /** * Returns true if the field is empty in the passed data array * - * @param mixed $data value to check against + * @param mixed $data Value to check against. * @return bool + * @deprecated 3.7.0 Use isEmpty() instead */ protected function _fieldIsEmpty($data) { - if (empty($data) && !is_bool($data) && !is_numeric($data)) { + return $this->isEmpty($data, static::EMPTY_ALL); + } + + /** + * Returns true if the field is empty in the passed data array + * + * @param mixed $data Value to check against. + * @param int $flags A bitmask of EMPTY_* flags which specify what is empty + * @return bool + */ + protected function isEmpty($data, $flags) + { + if ($data === null) { return true; } - $isArray = is_array($data); - if ($isArray && (isset($data['year']) || isset($data['hour']))) { - $value = implode('', $data); - return strlen($value) === 0; + if ($data === '' && ($flags & self::EMPTY_STRING)) { + return true; } - if ($isArray && isset($data['name'], $data['type'], $data['tmp_name'], $data['error'])) { - return (int)$data['error'] === UPLOAD_ERR_NO_FILE; + + $arrayTypes = self::EMPTY_ARRAY | self::EMPTY_DATE | self::EMPTY_TIME; + if ($data === [] && ($flags & $arrayTypes)) { + return true; + } + + if (is_array($data)) { + if (($flags & self::EMPTY_FILE) + && isset($data['name'], $data['type'], $data['tmp_name'], $data['error']) + && (int)$data['error'] === UPLOAD_ERR_NO_FILE + ) { + return true; + } + + $allFieldsAreEmpty = true; + foreach ($data as $field) { + if ($field !== null && $field !== '') { + $allFieldsAreEmpty = false; + break; + } + } + + if ($allFieldsAreEmpty) { + if (($flags & self::EMPTY_DATE) && isset($data['year'])) { + return true; + } + + if (($flags & self::EMPTY_TIME) && isset($data['hour'])) { + return true; + } + } } return false; @@ -2095,6 +2649,7 @@ public function __debugInfo() return [ '_presenceMessages' => $this->_presenceMessages, '_allowEmptyMessages' => $this->_allowEmptyMessages, + '_allowEmptyFlags' => $this->_allowEmptyFlags, '_useI18n' => $this->_useI18n, '_providers' => array_keys($this->_providers), '_fields' => $fields diff --git a/app/vendor/cakephp/cakephp/src/View/AjaxView.php b/app/vendor/cakephp/cakephp/src/View/AjaxView.php index b63412ad8..6538fc1f2 100644 --- a/app/vendor/cakephp/cakephp/src/View/AjaxView.php +++ b/app/vendor/cakephp/cakephp/src/View/AjaxView.php @@ -27,10 +27,9 @@ class AjaxView extends View { /** - * - * @var string + * {@inheritDoc} */ - public $layout = 'ajax'; + protected $layout = 'ajax'; /** * Constructor diff --git a/app/vendor/cakephp/cakephp/src/View/Cell.php b/app/vendor/cakephp/cakephp/src/View/Cell.php index 87af43595..d273dd4a1 100644 --- a/app/vendor/cakephp/cakephp/src/View/Cell.php +++ b/app/vendor/cakephp/cakephp/src/View/Cell.php @@ -43,27 +43,11 @@ abstract class Cell /** * Instance of the View created during rendering. Won't be set until after - * Cell::__toString() is called. + * Cell::__toString()/render() is called. * * @var \Cake\View\View - * @deprecated 3.1.0 Use createView() instead. */ - public $View; - - /** - * Name of the template that will be rendered. - * This property is inflected from the action name that was invoked. - * - * @var string - */ - public $template; - - /** - * Automatically set to the name of a plugin. - * - * @var string - */ - public $plugin; + protected $View; /** * An instance of a Cake\Http\ServerRequest object that contains information about the current request. @@ -72,43 +56,35 @@ abstract class Cell * * @var \Cake\Http\ServerRequest */ - public $request; + protected $request; /** * An instance of a Response object that contains information about the impending response * * @var \Cake\Http\Response */ - public $response; - - /** - * The helpers this cell uses. - * - * This property is copied automatically when using the CellTrait - * - * @var array - */ - public $helpers = []; + protected $response; /** * The cell's action to invoke. * * @var string */ - public $action; + protected $action; /** * Arguments to pass to cell's action. * * @var array */ - public $args = []; + protected $args = []; /** * These properties can be set directly on Cell and passed to the View as options. * * @var array * @see \Cake\View\View + * @deprecated 3.7.0 Use ViewBuilder::setOptions() or any one of it's setter methods instead. */ protected $_validViewOptions = [ 'viewPath' @@ -203,19 +179,11 @@ public function render($template = null) )); } - $builder = $this->viewBuilder(); + $builder = $this->viewBuilder()->setLayout(false); - if ($template !== null && - strpos($template, '/') === false && - strpos($template, '.') === false - ) { - $template = Inflector::underscore($template); - } - if ($template === null) { - $template = $builder->getTemplate() ?: $this->template; + if ($template !== null) { + $builder->setTemplate($template); } - $builder->setLayout(false) - ->setTemplate($template); $className = get_class($this); $namePrefix = '\View\Cell\\'; @@ -224,12 +192,16 @@ public function render($template = null) if (!$builder->getTemplatePath()) { $builder->setTemplatePath('Cell' . DIRECTORY_SEPARATOR . str_replace('\\', DIRECTORY_SEPARATOR, $name)); } + $template = $builder->getTemplate(); $this->View = $this->createView(); try { return $this->View->render($template); } catch (MissingTemplateException $e) { - throw new MissingCellViewException(['file' => $template, 'name' => $name], null, $e); + $attributes = $e->getAttributes(); + $attributes = ['file' => basename($attributes['file']), 'name' => $name]; + + throw new MissingCellViewException($attributes, null, $e); } }; @@ -292,6 +264,90 @@ public function __toString() } } + /** + * Magic accessor for removed properties. + * + * @param string $name Property name + * @return mixed + */ + public function __get($name) + { + $deprecated = [ + 'template' => 'getTemplate', + 'plugin' => 'getPlugin', + 'helpers' => 'getHelpers', + ]; + if (isset($deprecated[$name])) { + $method = $deprecated[$name]; + deprecationWarning(sprintf( + 'Cell::$%s is deprecated. Use $cell->viewBuilder()->%s() instead.', + $name, + $method + )); + + return $this->viewBuilder()->{$method}(); + } + + $protected = [ + 'action', + 'args', + 'request', + 'response', + 'View', + ]; + if (in_array($name, $protected, true)) { + deprecationWarning(sprintf( + 'Cell::$%s is now protected and shouldn\'t be accessed from outside a child class.', + $name + )); + } + + return $this->{$name}; + } + + /** + * Magic setter for removed properties. + * + * @param string $name Property name. + * @param mixed $value Value to set. + * @return void + */ + public function __set($name, $value) + { + $deprecated = [ + 'template' => 'setTemplate', + 'plugin' => 'setPlugin', + 'helpers' => 'setHelpers', + ]; + if (isset($deprecated[$name])) { + $method = $deprecated[$name]; + deprecationWarning(sprintf( + 'Cell::$%s is deprecated. Use $cell->viewBuilder()->%s() instead.', + $name, + $method + )); + $this->viewBuilder()->{$method}($value); + + return; + } + + $protected = [ + 'action', + 'args', + 'request', + 'response', + 'View', + ]; + if (in_array($name, $protected, true)) { + deprecationWarning(sprintf( + 'Cell::$%s is now protected and shouldn\'t be accessed from outside a child class.', + $name + )); + } + + $this->{$name} = $value; + } + /** * Debug info. * @@ -300,12 +356,11 @@ public function __toString() public function __debugInfo() { return [ - 'plugin' => $this->plugin, 'action' => $this->action, 'args' => $this->args, - 'template' => $this->template, 'request' => $this->request, 'response' => $this->response, + 'viewBuilder' => $this->viewBuilder(), ]; } } diff --git a/app/vendor/cakephp/cakephp/src/View/CellTrait.php b/app/vendor/cakephp/cakephp/src/View/CellTrait.php index cee1eff44..324ce8817 100644 --- a/app/vendor/cakephp/cakephp/src/View/CellTrait.php +++ b/app/vendor/cakephp/cakephp/src/View/CellTrait.php @@ -94,15 +94,15 @@ protected function _createCell($className, $action, $plugin, $options) { /* @var \Cake\View\Cell $instance */ $instance = new $className($this->request, $this->response, $this->getEventManager(), $options); - $instance->template = Inflector::underscore($action); $builder = $instance->viewBuilder(); + $builder->setTemplate(Inflector::underscore($action)); + if (!empty($plugin)) { $builder->setPlugin($plugin); } if (!empty($this->helpers)) { $builder->setHelpers($this->helpers); - $instance->helpers = $this->helpers; } if ($this instanceof View) { diff --git a/app/vendor/cakephp/cakephp/src/View/Form/ArrayContext.php b/app/vendor/cakephp/cakephp/src/View/Form/ArrayContext.php index 98b48c316..c307cb71f 100644 --- a/app/vendor/cakephp/cakephp/src/View/Form/ArrayContext.php +++ b/app/vendor/cakephp/cakephp/src/View/Form/ArrayContext.php @@ -16,6 +16,7 @@ use Cake\Http\ServerRequest; use Cake\Utility\Hash; +use Cake\Validation\Validator; /** * Provides a basic array based context provider for FormHelper. @@ -29,7 +30,8 @@ * will be used when there is no request data set. Data should be nested following * the dot separated paths you access your fields with. * - `required` A nested array of fields, relationships and boolean - * flags to indicate a field is required. + * flags to indicate a field is required. The value can also be a string to be used + * as the required error message * - `schema` An array of data that emulate the column structures that * Cake\Database\Schema\Schema uses. This array allows you to control * the inferred type for fields and allows auto generation of attributes @@ -53,7 +55,12 @@ * 'defaults' => [ * 'id' => 1, * 'title' => 'First post!', - * ] + * ], + * 'required' => [ + * 'id' => true, // will use default required message + * 'title' => 'Please enter a title', + * 'body' => false, + * ], * ]; * ``` */ @@ -194,16 +201,49 @@ public function val($field, $options = []) * @return bool */ public function isRequired($field) + { + return (bool)$this->getRequiredMessage($field); + } + + /** + * {@inheritDoc} + */ + public function getRequiredMessage($field) { if (!is_array($this->_context['required'])) { - return false; + return null; } $required = Hash::get($this->_context['required'], $field); if ($required === null) { $required = Hash::get($this->_context['required'], $this->stripNesting($field)); } - return (bool)$required; + if ($required === false) { + return null; + } + + if ($required === true) { + $required = __d('cake', 'This field is required'); + } + + return $required; + } + + /** + * Get field length from validation + * + * In this context class, this is simply defined by the 'length' array. + * + * @param string $field A dot separated path to check required-ness for. + * @return int|null + */ + public function getMaxLength($field) + { + if (!is_array($this->_context['schema'])) { + return null; + } + + return Hash::get($this->_context['schema'], "$field.length"); } /** @@ -286,7 +326,7 @@ public function error($field) return []; } - return Hash::get($this->_context['errors'], $field); + return (array)Hash::get($this->_context['errors'], $field); } /** diff --git a/app/vendor/cakephp/cakephp/src/View/Form/ContextInterface.php b/app/vendor/cakephp/cakephp/src/View/Form/ContextInterface.php index 3daa8f59c..a87e3ea1e 100644 --- a/app/vendor/cakephp/cakephp/src/View/Form/ContextInterface.php +++ b/app/vendor/cakephp/cakephp/src/View/Form/ContextInterface.php @@ -16,6 +16,9 @@ /** * Interface for FormHelper context implementations. + * + * @method string|null getRequiredMessage($field) Gets the default "required" error message for a field + * @method int|null getMaxLength($field) Get maximum length of a field from model validation */ interface ContextInterface { @@ -73,7 +76,7 @@ public function isRequired($field); /** * Get the fieldnames of the top level object in this context. * - * @return array A list of the field names in the context. + * @return string[] A list of the field names in the context. */ public function fieldNames(); diff --git a/app/vendor/cakephp/cakephp/src/View/Form/EntityContext.php b/app/vendor/cakephp/cakephp/src/View/Form/EntityContext.php index 871d9b46f..52812391e 100644 --- a/app/vendor/cakephp/cakephp/src/View/Form/EntityContext.php +++ b/app/vendor/cakephp/cakephp/src/View/Form/EntityContext.php @@ -249,7 +249,7 @@ public function val($field, $options = []) } if ($entity instanceof EntityInterface) { - $part = array_pop($parts); + $part = end($parts); $val = $entity->get($part); if ($val !== null) { return $val; @@ -261,7 +261,7 @@ public function val($field, $options = []) return $options['default']; } - return $this->_schemaDefault($part, $entity); + return $this->_schemaDefault($parts); } if (is_array($entity) || $entity instanceof ArrayAccess) { $key = array_pop($parts); @@ -275,16 +275,16 @@ public function val($field, $options = []) /** * Get default value from table schema for given entity field. * - * @param string $field Field name. - * @param \Cake\Datasource\EntityInterface $entity The entity. + * @param array $parts Each one of the parts in a path for a field name * @return mixed */ - protected function _schemaDefault($field, $entity) + protected function _schemaDefault($parts) { - $table = $this->_getTable($entity); + $table = $this->_getTable($parts); if ($table === false) { return null; } + $field = end($parts); $defaults = $table->getSchema()->defaultValues(); if (!array_key_exists($field, $defaults)) { return null; @@ -313,11 +313,12 @@ protected function _extractMultiple($values, $path) } /** - * Fetch the leaf entity for the given path. + * Fetch the entity or data value for a given path + * + * This method will traverse the given path and find the entity + * or array value for a given path. * - * This method will traverse the given path and find the leaf - * entity. If the path does not contain a leaf entity false - * will be returned. + * If you only want the terminal Entity for a path use `leafEntity` instead. * * @param array|null $path Each one of the parts in a path for a field name * or null to get the entity passed in constructor context. @@ -349,7 +350,6 @@ public function entity($path = null) $prop = $path[$i]; $next = $this->_getProp($entity, $prop); $isLast = ($i === $last); - if (!$isLast && $next === null && $prop !== '_ids') { $table = $this->_getTable($path); @@ -372,6 +372,74 @@ public function entity($path = null) )); } + /** + * Fetch the terminal or leaf entity for the given path. + * + * Traverse the path until an entity cannot be found. Lists containing + * entities will be traversed if the first element contains an entity. + * Otherwise the containing Entity will be assumed to be the terminal one. + * + * @param array|null $path Each one of the parts in a path for a field name + * or null to get the entity passed in constructor context. + * @return array Containing the found entity, and remaining un-matched path. + * @throws \RuntimeException When properties cannot be read. + */ + protected function leafEntity($path = null) + { + if ($path === null) { + return $this->_context['entity']; + } + + $oneElement = count($path) === 1; + if ($oneElement && $this->_isCollection) { + throw new RuntimeException(sprintf( + 'Unable to fetch property "%s"', + implode('.', $path) + )); + } + $entity = $this->_context['entity']; + if ($oneElement) { + return [$entity, $path]; + } + + if ($path[0] === $this->_rootName) { + $path = array_slice($path, 1); + } + + $len = count($path); + $last = $len - 1; + $leafEntity = $entity; + for ($i = 0; $i < $len; $i++) { + $prop = $path[$i]; + $next = $this->_getProp($entity, $prop); + + // Did not dig into an entity, return the current one. + if (is_array($entity) && !($next instanceof EntityInterface || $next instanceof Traversable)) { + return [$leafEntity, array_slice($path, $i - 1)]; + } + + if ($next instanceof EntityInterface) { + $leafEntity = $next; + } + + // If we are at the end of traversable elements + // return the last entity found. + $isTraversable = ( + is_array($next) || + $next instanceof Traversable || + $next instanceof EntityInterface + ); + if (!$isTraversable) { + return [$leafEntity, array_slice($path, $i)]; + } + $entity = $next; + } + throw new RuntimeException(sprintf( + 'Unable to fetch property "%s"', + implode('.', $path) + )); + } + /** * Read property values or traverse arrays/iterators. * @@ -426,6 +494,57 @@ public function isRequired($field) return false; } + /** + * {@inheritDoc} + */ + public function getRequiredMessage($field) + { + $parts = explode('.', $field); + + $validator = $this->_getValidator($parts); + $fieldName = array_pop($parts); + if (!$validator->hasField($fieldName)) { + return null; + } + + $ruleset = $validator->field($fieldName); + + $requiredMessage = $validator->getRequiredMessage($fieldName); + $emptyMessage = $validator->getNotEmptyMessage($fieldName); + + if ($ruleset->isPresenceRequired() && $requiredMessage) { + return $requiredMessage; + } + if (!$ruleset->isEmptyAllowed() && $emptyMessage) { + return $emptyMessage; + } + + return null; + } + + /** + * Get field length from validation + * + * @param string $field The dot separated path to the field you want to check. + * @return int|null + */ + public function getMaxLength($field) + { + $parts = explode('.', $field); + $validator = $this->_getValidator($parts); + $fieldName = array_pop($parts); + if (!$validator->hasField($fieldName)) { + return null; + } + foreach ($validator->field($fieldName)->rules() as $rule) { + if ($rule->get('rule') === 'maxLength') { + return $rule->get('pass')[0]; + } + } + + return null; + } + /** * Get the field names from the top level entity. * @@ -581,9 +700,21 @@ public function hasError($field) public function error($field) { $parts = explode('.', $field); - $entity = $this->entity($parts); + try { + list($entity, $remainingParts) = $this->leafEntity($parts); + } catch (RuntimeException $e) { + return []; + } + if (count($remainingParts) === 0) { + return $entity->getErrors(); + } if ($entity instanceof EntityInterface) { + $error = $entity->getError(implode('.', $remainingParts)); + if ($error) { + return $error; + } + return $entity->getError(array_pop($parts)); } diff --git a/app/vendor/cakephp/cakephp/src/View/Form/FormContext.php b/app/vendor/cakephp/cakephp/src/View/Form/FormContext.php index e1616014e..494be6b27 100644 --- a/app/vendor/cakephp/cakephp/src/View/Form/FormContext.php +++ b/app/vendor/cakephp/cakephp/src/View/Form/FormContext.php @@ -94,6 +94,11 @@ public function val($field, $options = []) return $val; } + $val = $this->_form->getData($field); + if ($val !== null) { + return $val; + } + if ($options['default'] !== null || !$options['schemaDefault']) { return $options['default']; } @@ -105,7 +110,6 @@ public function val($field, $options = []) * Get default value from form schema for given field. * * @param string $field Field name. - * @return mixed */ protected function _schemaDefault($field) @@ -134,6 +138,52 @@ public function isRequired($field) return false; } + /** + * {@inheritDoc} + */ + public function getRequiredMessage($field) + { + $parts = explode('.', $field); + + $validator = $this->_form->getValidator(); + $fieldName = array_pop($parts); + if (!$validator->hasField($fieldName)) { + return null; + } + + $ruleset = $validator->field($fieldName); + + $requiredMessage = $validator->getRequiredMessage($fieldName); + $emptyMessage = $validator->getNotEmptyMessage($fieldName); + + if ($ruleset->isPresenceRequired() && $requiredMessage) { + return $requiredMessage; + } + if (!$ruleset->isEmptyAllowed() && $emptyMessage) { + return $emptyMessage; + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getMaxLength($field) + { + $validator = $this->_form->getValidator(); + if (!$validator->hasField($field)) { + return null; + } + foreach ($validator->field($field)->rules() as $rule) { + if ($rule->get('rule') === 'maxLength') { + return $rule->get('pass')[0]; + } + } + + return null; + } + /** * {@inheritDoc} */ @@ -176,6 +226,6 @@ public function hasError($field) */ public function error($field) { - return array_values((array)Hash::get($this->_form->errors(), $field, [])); + return (array)Hash::get($this->_form->getErrors(), $field, []); } } diff --git a/app/vendor/cakephp/cakephp/src/View/Form/NullContext.php b/app/vendor/cakephp/cakephp/src/View/Form/NullContext.php index f2e38b11e..c05c63ee4 100644 --- a/app/vendor/cakephp/cakephp/src/View/Form/NullContext.php +++ b/app/vendor/cakephp/cakephp/src/View/Form/NullContext.php @@ -83,6 +83,22 @@ public function isRequired($field) return false; } + /** + * {@inheritDoc} + */ + public function getRequiredMessage($field) + { + return null; + } + + /** + * {@inheritDoc} + */ + public function getMaxLength($field) + { + return null; + } + /** * {@inheritDoc} */ diff --git a/app/vendor/cakephp/cakephp/src/View/Helper.php b/app/vendor/cakephp/cakephp/src/View/Helper.php index 9f0e9b3ff..765d0d6c8 100644 --- a/app/vendor/cakephp/cakephp/src/View/Helper.php +++ b/app/vendor/cakephp/cakephp/src/View/Helper.php @@ -48,7 +48,7 @@ class Helper implements EventListenerInterface * * @var array */ - public $helpers = []; + protected $helpers = []; /** * Default config for this helper. @@ -65,38 +65,18 @@ class Helper implements EventListenerInterface protected $_helperMap = []; /** - * The current theme name if any. - * - * @var string - */ - public $theme; - - /** - * Request object - * - * @var \Cake\Http\ServerRequest - */ - public $request; - - /** - * Plugin path - * - * @var string - */ - public $plugin; - - /** - * Holds the fields ['field_name' => ['type' => 'string', 'length' => 100]], - * primaryKey and validates ['field_name'] + * Unused. * * @var array + * @deprecated 3.7.0 This property is unused and will be removed in 4.0.0. */ public $fieldset = []; /** - * Holds tag templates. + * Unused. * * @var array + * @deprecated 3.7.0 This property is unused and will be removed in 4.0.0. */ public $tags = []; @@ -116,8 +96,6 @@ class Helper implements EventListenerInterface public function __construct(View $View, array $config = []) { $this->_View = $View; - $this->request = $View->request; - $this->setConfig($config); if (!empty($this->helpers)) { @@ -153,6 +131,81 @@ public function __get($name) return $this->{$name}; } + + $removed = [ + 'theme' => 'getTheme', + 'plugin' => 'getPlugin', + ]; + if (isset($removed[$name])) { + $method = $removed[$name]; + deprecationWarning(sprintf( + 'Helper::$%s is deprecated. Use $view->%s() instead.', + $name, + $method + )); + + return $this->_View->{$method}(); + } + + if ($name === 'request') { + deprecationWarning( + 'Helper::$request is deprecated. Use $helper->getView()->getRequest() instead.' + ); + + return $this->_View->getRequest(); + } + + if ($name === 'helpers') { + deprecationWarning( + 'Helper::$helpers is now protected and should not be accessed from outside a helper class.' + ); + + return $this->helpers; + } + } + + /** + * Magic setter for removed properties. + * + * @param string $name Property name. + * @param mixed $value Value to set. + * @return void + */ + public function __set($name, $value) + { + $removed = [ + 'theme' => 'setTheme', + 'plugin' => 'setPlugin', + ]; + if (isset($removed[$name])) { + $method = $removed[$name]; + deprecationWarning(sprintf( + 'Helper::$%s is deprecated. Use $view->%s() instead.', + $name, + $method + )); + $this->_View->{$method}($value); + + return; + } + + if ($name === 'request') { + deprecationWarning( + 'Helper::$request is deprecated. Use $helper->getView()->setRequest() instead.' + ); + + $this->_View->setRequest($value); + + return; + } + + if ($name === 'helpers') { + deprecationWarning( + 'Helper::$helpers is now protected and should not be accessed from outside a helper class.' + ); + } + + $this->{$name} = $value; } /** @@ -176,7 +229,7 @@ public function getView() */ protected function _confirm($message, $okCode, $cancelCode = '', $options = []) { - $message = str_replace('\\\n', '\n', json_encode($message)); + $message = $this->_cleanConfirmMessage($message); $confirm = "if (confirm({$message})) { {$okCode} } {$cancelCode}"; // We cannot change the key here in 3.x, but the behavior is inverted in this case $escape = isset($options['escape']) && $options['escape'] === false; @@ -188,6 +241,17 @@ protected function _confirm($message, $okCode, $cancelCode = '', $options = []) return $confirm; } + /** + * Returns a string read to be used in confirm() + * + * @param string $message The message to clean + * @return mixed + */ + protected function _cleanConfirmMessage($message) + { + return str_replace('\\\n', '\n', json_encode($message)); + } + /** * Adds the given class to the element options * @@ -262,10 +326,6 @@ public function __debugInfo() { return [ 'helpers' => $this->helpers, - 'theme' => $this->theme, - 'plugin' => $this->plugin, - 'fieldset' => $this->fieldset, - 'tags' => $this->tags, 'implementedEvents' => $this->implementedEvents(), '_config' => $this->getConfig(), ]; diff --git a/app/vendor/cakephp/cakephp/src/View/Helper/FlashHelper.php b/app/vendor/cakephp/cakephp/src/View/Helper/FlashHelper.php index 555c20f5a..f47aea38f 100644 --- a/app/vendor/cakephp/cakephp/src/View/Helper/FlashHelper.php +++ b/app/vendor/cakephp/cakephp/src/View/Helper/FlashHelper.php @@ -70,18 +70,20 @@ class FlashHelper extends Helper */ public function render($key = 'flash', array $options = []) { - if (!$this->request->getSession()->check("Flash.$key")) { + $session = $this->_View->getRequest()->getSession(); + + if (!$session->check("Flash.$key")) { return null; } - $flash = $this->request->getSession()->read("Flash.$key"); + $flash = $session->read("Flash.$key"); if (!is_array($flash)) { throw new UnexpectedValueException(sprintf( 'Value for flash setting key "%s" must be an array.', $key )); } - $this->request->getSession()->delete("Flash.$key"); + $session->delete("Flash.$key"); $out = ''; foreach ($flash as $message) { diff --git a/app/vendor/cakephp/cakephp/src/View/Helper/FormHelper.php b/app/vendor/cakephp/cakephp/src/View/Helper/FormHelper.php index c9040c27e..ffdb236bc 100644 --- a/app/vendor/cakephp/cakephp/src/View/Helper/FormHelper.php +++ b/app/vendor/cakephp/cakephp/src/View/Helper/FormHelper.php @@ -40,7 +40,6 @@ * @method string email($fieldName, array $options = []) * @method string password($fieldName, array $options = []) * @method string search($fieldName, array $options = []) - * * @property \Cake\View\Helper\HtmlHelper $Html * @property \Cake\View\Helper\UrlHelper $Url * @link https://book.cakephp.org/3.0/en/views/helpers/form.html @@ -163,7 +162,11 @@ class FormHelper extends Helper 'textarea' => '', // Container for submit buttons. 'submitContainer' => '
{{content}}
', - ] + //Confirm javascript template for postLink() + 'confirmJs' => '{{confirm}}', + ], + // set HTML5 validation message to custom required/empty messages + 'autoSetCustomValidity' => false, ]; /** @@ -213,7 +216,7 @@ class FormHelper extends Helper * * @see \Cake\View\Helper\FormHelper::_secure() * @see \Cake\Controller\Component\SecurityComponent::validatePost() - * @var array + * @var string[] */ protected $_unlockedFields = []; @@ -249,14 +252,14 @@ class FormHelper extends Helper /** * The sources to be used when retrieving prefilled input values. * - * @var array + * @var string[] */ protected $_valueSources = ['context']; /** * Grouped input types. * - * @var array + * @var string[] */ protected $_groupedInputTypes = ['radio', 'multicheckbox', 'date', 'time', 'datetime']; @@ -447,7 +450,7 @@ public function create($context = null, array $options = []) unset($options['templates']); if ($options['action'] === false || $options['url'] === false) { - $url = $this->request->getRequestTarget(); + $url = $this->_View->getRequest()->getRequestTarget(); $action = null; } else { $url = $this->_formUrl($context, $options); @@ -525,8 +528,10 @@ public function create($context = null, array $options = []) */ protected function _formUrl($context, $options) { + $request = $this->_View->getRequest(); + if ($options['action'] === null && $options['url'] === null) { - return $this->request->getRequestTarget(); + return $request->getRequestTarget(); } if (is_string($options['url']) || @@ -540,9 +545,9 @@ protected function _formUrl($context, $options) } $actionDefaults = [ - 'plugin' => $this->plugin, - 'controller' => $this->request->getParam('controller'), - 'action' => $this->request->getParam('action'), + 'plugin' => $this->_View->getPlugin(), + 'controller' => $request->getParam('controller'), + 'action' => $request->getParam('action'), ]; $action = (array)$options['url'] + $actionDefaults; @@ -569,7 +574,9 @@ protected function _lastAction($url) $action = Router::url($url, true); $query = parse_url($action, PHP_URL_QUERY); $query = $query ? '?' . $query : ''; - $this->_lastAction = parse_url($action, PHP_URL_PATH) . $query; + + $path = parse_url($action, PHP_URL_PATH) ?: ''; + $this->_lastAction = $path . $query; } /** @@ -581,17 +588,19 @@ protected function _lastAction($url) */ protected function _csrfField() { - if ($this->request->getParam('_Token.unlockedFields')) { - foreach ((array)$this->request->getParam('_Token.unlockedFields') as $unlocked) { + $request = $this->_View->getRequest(); + + if ($request->getParam('_Token.unlockedFields')) { + foreach ((array)$request->getParam('_Token.unlockedFields') as $unlocked) { $this->_unlockedFields[] = $unlocked; } } - if (!$this->request->getParam('_csrfToken')) { + if (!$request->getParam('_csrfToken')) { return ''; } return $this->hidden('_csrfToken', [ - 'value' => $this->request->getParam('_csrfToken'), + 'value' => $request->getParam('_csrfToken'), 'secure' => static::SECURE_SKIP, 'autocomplete' => 'off', ]); @@ -612,7 +621,7 @@ public function end(array $secureAttributes = []) { $out = ''; - if ($this->requestType !== 'get' && $this->request->getParam('_Token')) { + if ($this->requestType !== 'get' && $this->_View->getRequest()->getParam('_Token')) { $out .= $this->secure($this->fields, $secureAttributes); $this->fields = []; $this->_unlockedFields = []; @@ -645,7 +654,7 @@ public function end(array $secureAttributes = []) */ public function secure(array $fields = [], array $secureAttributes = []) { - if (!$this->request->getParam('_Token')) { + if (!$this->_View->getRequest()->getParam('_Token')) { return ''; } $debugSecurity = Configure::read('debug'); @@ -698,10 +707,10 @@ public function unlockField($name = null) if ($name === null) { return $this->_unlockedFields; } - if (!in_array($name, $this->_unlockedFields)) { + if (!in_array($name, $this->_unlockedFields, true)) { $this->_unlockedFields[] = $name; } - $index = array_search($name, $this->fields); + $index = array_search($name, $this->fields, true); if ($index !== false) { unset($this->fields[$index]); } @@ -740,7 +749,7 @@ protected function _secure($lock, $field, $value = null) $field = preg_replace('/(\.\d+)+$/', '', $field); if ($lock) { - if (!in_array($field, $this->fields)) { + if (!in_array($field, $this->fields, true)) { if ($value !== null) { $this->fields[$field] = $value; @@ -796,7 +805,7 @@ public function error($field, $text = null, array $options = []) if (!$context->hasError($field)) { return ''; } - $error = (array)$context->error($field); + $error = $context->error($field); if (is_array($text)) { $tmp = []; @@ -1094,7 +1103,7 @@ public function fieldset($fields = '', array $options = []) if ($legend === true) { $isCreate = $context->isCreate(); - $modelName = Inflector::humanize(Inflector::singularize($this->request->getParam('controller'))); + $modelName = Inflector::humanize(Inflector::singularize($this->_View->getRequest()->getParam('controller'))); if (!$isCreate) { $legend = __d('cake', 'Edit {0}', $modelName); } else { @@ -1443,10 +1452,28 @@ protected function _magicOptions($fieldName, $options, $allowOverride) { $context = $this->_getContext(); + $options += [ + 'templateVars' => [] + ]; + if (!isset($options['required']) && $options['type'] !== 'hidden') { $options['required'] = $context->isRequired($fieldName); } + if (method_exists($context, 'getRequiredMessage')) { + $message = $context->getRequiredMessage($fieldName); + $message = h($message); + + if ($options['required'] && $message) { + $options['templateVars']['customValidityMessage'] = $message; + + if ($this->getConfig('autoSetCustomValidity')) { + $options['oninvalid'] = "this.setCustomValidity(''); if (!this.validity.valid) this.setCustomValidity('$message')"; + $options['oninput'] = "this.setCustomValidity('')"; + } + } + } + $type = $context->type($fieldName); $fieldDef = $context->attributes($fieldName); @@ -1476,13 +1503,22 @@ protected function _magicOptions($fieldName, $options, $allowOverride) unset($options['step']); } - $autoLength = !array_key_exists('maxlength', $options) - && !empty($fieldDef['length']) - && $options['type'] !== 'select'; + $typesWithMaxLength = ['text', 'textarea', 'email', 'tel', 'url', 'search']; + if (!array_key_exists('maxlength', $options) + && in_array($options['type'], $typesWithMaxLength) + ) { + $maxLength = null; + if (method_exists($context, 'getMaxLength')) { + $maxLength = $context->getMaxLength($fieldName); + } + + if ($maxLength === null && !empty($fieldDef['length'])) { + $maxLength = $fieldDef['length']; + } - $allowedTypes = ['text', 'textarea', 'email', 'tel', 'url', 'search']; - if ($autoLength && in_array($options['type'], $allowedTypes)) { - $options['maxlength'] = min($fieldDef['length'], 100000); + if ($maxLength !== null) { + $options['maxlength'] = min($maxLength, 100000); + } } if (in_array($options['type'], ['datetime', 'date', 'time', 'select'])) { @@ -1952,11 +1988,16 @@ public function postLink($title, $url = null, array $options = []) $url = '#'; $onClick = 'document.' . $formName . '.submit();'; if ($confirmMessage) { - $options['onclick'] = $this->_confirm($confirmMessage, $onClick, '', $options); + $confirm = $this->_confirm($confirmMessage, $onClick, '', $options); } else { - $options['onclick'] = $onClick . ' '; + $confirm = $onClick . ' '; } - $options['onclick'] .= 'event.returnValue = false; return false;'; + $confirm .= 'event.returnValue = false; return false;'; + $options['onclick'] = $this->templater()->format('confirmJs', [ + 'confirmMessage' => $this->_cleanConfirmMessage($confirmMessage), + 'formName' => $formName, + 'confirm' => $confirm + ]); $out .= $this->Html->link($title, $url, $options); @@ -2021,7 +2062,7 @@ public function submit($caption = null, array $options = []) if ($isUrl) { $options['src'] = $caption; } elseif ($isImage) { - if ($caption{0} !== '/') { + if ($caption[0] !== '/') { $url = $this->Url->webroot(Configure::read('App.imageBaseUrl') . $caption); } else { $url = $this->Url->webroot(trim($caption, '/')); @@ -2629,7 +2670,7 @@ public function date($fieldName, array $options = []) protected function _initInputField($field, $options = []) { if (!isset($options['secure'])) { - $options['secure'] = (bool)$this->request->getParam('_Token'); + $options['secure'] = (bool)$this->_View->getRequest()->getParam('_Token'); } $context = $this->_getContext(); @@ -2805,7 +2846,8 @@ protected function _getContext($data = []) } $data += ['entity' => null]; - return $this->_context = $this->contextFactory()->get($this->request, $data); + return $this->_context = $this->contextFactory() + ->get($this->_View->getRequest(), $data); } /** @@ -2880,7 +2922,7 @@ public function implementedEvents() * * Returns a list, but at least one item, of valid sources, such as: `'context'`, `'data'` and `'query'`. * - * @return array List of value sources. + * @return string[] List of value sources. */ public function getValueSources() { @@ -2893,7 +2935,7 @@ public function getValueSources() * Valid values are `'context'`, `'data'` and `'query'`. * You need to supply one valid context or multiple, as a list of strings. Order sets priority. * - * @param string|array $sources A string or a list of strings identifying a source. + * @param string|string[] $sources A string or a list of strings identifying a source. * @return $this */ public function setValueSources($sources) @@ -2925,7 +2967,7 @@ public function getSourceValue($fieldname, $options = []) } if (isset($valueMap[$valuesSource])) { $method = $valueMap[$valuesSource]; - $value = $this->request->{$method}($fieldname); + $value = $this->_View->getRequest()->{$method}($fieldname); if ($value !== null) { return $value; } diff --git a/app/vendor/cakephp/cakephp/src/View/Helper/HtmlHelper.php b/app/vendor/cakephp/cakephp/src/View/Helper/HtmlHelper.php index f8f21fbce..c27745aea 100644 --- a/app/vendor/cakephp/cakephp/src/View/Helper/HtmlHelper.php +++ b/app/vendor/cakephp/cakephp/src/View/Helper/HtmlHelper.php @@ -81,7 +81,8 @@ class HtmlHelper extends Helper 'javascriptblock' => '{{content}}', 'javascriptstart' => '', - 'javascriptend' => '' + 'javascriptend' => '', + 'confirmJs' => '{{confirm}}' ] ]; @@ -110,7 +111,7 @@ class HtmlHelper extends Helper /** * Document type definitions * - * @var array + * @var string[] */ protected $_docTypes = [ 'html4-strict' => '', @@ -142,7 +143,7 @@ class HtmlHelper extends Helper public function __construct(View $View, array $config = []) { parent::__construct($View, $config); - $this->response = $this->_View->response ?: new Response(); + $this->response = $this->_View->getResponse() ?: new Response(); } /** @@ -373,17 +374,20 @@ public function link($title, $url = null, array $options = []) $title = htmlentities($title, ENT_QUOTES, $escapeTitle); } + $templater = $this->templater(); $confirmMessage = null; if (isset($options['confirm'])) { $confirmMessage = $options['confirm']; unset($options['confirm']); } if ($confirmMessage) { - $options['onclick'] = $this->_confirm($confirmMessage, 'return true;', 'return false;', $options); + $confirm = $this->_confirm($confirmMessage, 'return true;', 'return false;', $options); + $options['onclick'] = $templater->format('confirmJs', [ + 'confirmMessage' => $this->_cleanConfirmMessage($confirmMessage), + 'confirm' => $confirm + ]); } - $templater = $this->templater(); - return $templater->format('link', [ 'url' => $url, 'attrs' => $templater->formatAttributes($options), @@ -431,7 +435,7 @@ public function link($title, $url = null, array $options = []) * - `rel` Defaults to 'stylesheet'. If equal to 'import' the stylesheet will be imported. * - `fullBase` If true the URL will get a full address for the css file. * - * @param string|array $path The name of a CSS style sheet or an array containing names of + * @param string|string[] $path The name of a CSS style sheet or an array containing names of * CSS stylesheets. If `$path` is prefixed with '/', the path will be relative to the webroot * of your application. Otherwise, the path will be relative to your CSS path, usually webroot/css. * @param array $options Array of options and HTML arguments. @@ -525,7 +529,7 @@ public function css($path, array $options = []) * - `plugin` False value will prevent parsing path as a plugin * - `fullBase` If true the url will get a full address for the script file. * - * @param string|array $url String or array of javascript files to include + * @param string|string[] $url String or array of javascript files to include * @param array $options Array of options, and html attributes see above. * @return string|null String of `